var IMG_ROOTICON		= "/cordys/wcp/theme/default/icon/interface/folder.png";
var IMG_OPENROOTICON	= "/cordys/wcp/theme/default/icon/interface/folderopen.png";
var IMG_FOLDERICON		= "/cordys/wcp/theme/default/icon/interface/folder.png";
var IMG_OPENFOLDERICON	= "/cordys/wcp/theme/default/icon/interface/folderopen.png";
var IMG_FILEICON		= "/cordys/wcp/theme/default/icon/interface/item.png";

//used style selectors
var CL_CONTAINER_RIGHTFOLDING	= "treecontainer rightfolding";
var CL_CONTAINER_LEFTFOLDING	= "treecontainer leftfolding";
var CL_HOVER					= "treehover";
var CL_HOVER_PLUSMIN			= "treehoverplusmin";
var CL_EXPANDED					= "treeexpanded";
var CL_COLLAPSED				= "treecollapsed";
var CL_CONNECTLINE				= "treeconnectline";
var CL_ITEMCHECKBOX				= "treeitemcheckbox";
var CL_ITEMINPUT				= "treeiteminput";
var CL_ITEMSTANDALONEEXPANDED	= "treeitemstandaloneexpanded";
var CL_ITEMSTANDALONECOLLAPSED	= "treeitemstandalonecollapsed";
var CL_ITEMEXPANDED				= "treeitemexpanded";
var CL_ITEMEXPANDEDLAST			= "treeitemexpandedlast";
var CL_ITEMCOLLAPSED			= "treeitemcollapsed";
var CL_ITEMCOLLAPSEDLAST		= "treeitemcollapsedlast";
var CL_ITEMLEAF					= "treeitemleaf";
var CL_ITEMLEAFLAST				= "treeitemleaflast";
var CL_HIGHLIGHT				= "highlight";
var CL_OUTFOCUS					= "outfocus";
var CL_IMAGECONTAINER			= "imageholderitem bgsmall";
var CL_ITEM_SMALL				= "item";
var CL_ITEMDESCRIPTION			= "treeitemdescription";

//var TREE_LIBRARY = "wcp.library.ui.Tree";
var EXPANDED = "expanded";
var COLLAPSED = "collapsed";
var MOVE_HAND_MARGIN_X = 25;
var MOVE_HAND_MARGIN_Y = 15;
var objectCounter = 1;

// Make the global Docex namespace available
Docex = parent.Docex || top.Docex;
var activeTree = null;
var gDraggedTreeItem = null;
var xmlDocument;
var copypasteTreeData;
var dataTransfer = {};

var	_fnselectXMLNode;
var	_fnselectXMLNodes;
var	_fngetTextContent;
var	_fnsetTextContent;
var _fnaddListener;
var _fnremoveListener;
var _fngetEventTarget;
var _fngetNodeText;
var _fngetComputedStyle;

function Tree(){
    _fnselectXMLNode = Docex.selectXMLNode;
    _fnselectXMLNodes = Docex.selectXMLNodes;
    _fngetTextContent = Docex.getTextContent;
    _fnsetTextContent = Docex.setTextContent;
    _fnaddListener = Docex.addDOMListener;
    _fnremoveListener = Docex.removeDOMListener;
    _fngetEventTarget = Docex.getEventTarget;
    _fngetNodeText = Docex.getNodeText;
    _fngetComputedStyle = Docex.getComputedStyle;
}

Tree.attach = function(tree){
    var treeController = new Tree();
    var prototypes = Tree.prototype;
    for (var prototypeKey in prototypes) {
        if (typeof(prototypes[prototypeKey]) == "function") {
            tree[prototypeKey] = prototypes[prototypeKey];
        }
    }
    if (!window.ActiveXObject) {
        if (!tree.leftFolding) 
            tree.leftFolding = tree.getAttribute("leftFolding");
        if (!tree.treeData) 
            tree.treeData = tree.getAttribute("treeData");
        if (!tree.treeSchema) 
            tree.treeSchema = tree.getAttribute("treeSchema");
        if (!tree.BusDataIsland) 
            tree.BusDataIsland = tree.getAttribute("BusDataIsland");
    }
    tree.isQuirks = (document.compatMode == 'CSS1Compat') ? false : true;
    //left folding tree, expands to left, default false.
    var leftFolding = readProperty(tree.leftFolding);
    tree.leftFolding = leftFolding == null ? false : leftFolding;
    var saveNavigationPath = readProperty(tree.saveState);
    tree.saveState = (saveNavigationPath == null) ? false : saveNavigationPath;
    tree.lastExpandedItem = null;
    tree.goTree = null;
    tree.goTreeRoot = null;
    tree.selectedItem = null;
    tree.selectedItems = new Array();
    tree.currentTreeItem = null;
    tree.currentItemState = null;
    tree.inpField = null;
    tree.genericDropEnabled = false;
    tree.renameMode = false;
    //On onclick of any item in the page, the selection moves to some element in the page
    _fnaddListener(tree.ownerDocument, "onclick", moveFocus(tree));
    
    tree.tupleSearch = "";
    
    tree.TreeSchema = new Object();
    tree.TreeSchema.root = null;
    tree.TreeSchema.rootItem = null;
    tree.TreeSchema.nodeItems = new Array();
    tree.TreeSchema.useTreeLines = true;
    tree.TreeSchema.usePlusIcons = true;
    tree.TreeSchema.updateAllowed = "false";
    tree.TreeSchema.dragEnabled = "false";
    tree.TreeSchema.rootSearch = "";
    tree.TreeSchema.contSearch = "";
    tree.TreeSchema.leafSearch = "";
    tree.TreeSchema.treeWidth = 500; // width of tree 
    tree.TreeSchema.maxLevel = 1;
    tree.TreeSchema.xmlData = null;
    
    tree.gRecursive = false;
    tree.gDropEffect = null;
    tree.gDraggingItem = null;
    tree.gDropCurrent = null;
    tree.gDropPrevious = null;
    tree.gExprDropTarget = null;
    tree.active = false;
    _fnaddListener(tree.ownerDocument, "onkeydown", fnOnKeyDown(tree));
    _fnaddListener(tree.ownerDocument, "onselectstart", startselecting(tree));
    if (tree.treeSchema) 
        tree.setTreeSchema(tree.treeSchema);
    // if treeData is set as an inline property set it 
    else 
        if (tree.treeData) 
            tree.setTreeData(tree.treeData);
    tree.style.overflow = "auto";
    if (tree.leftFolding) {
        tree.scrollLeft = 99999;
    }
    if (tree.saveState) {
        tree.navigationPath = "lastExpandedItem:##";
    }
}

Tree.detach = function(tree){
    if (tree.saveState) {
        tree.lastExpandedItem = null;
    }
    var treeRoot = tree.firstChild;
    if (treeRoot) {
        treeRoot.onmousemove = null;
        treeRoot.onmouseout = null;
    }
    tree.genericDropEnabled = null;
    _fnremoveListener(tree.ownerDocument, "onclick", moveFocus(tree));
    tree.__moveFocus = null;
    _fnremoveListener(tree.ownerDocument, "onkeydown", fnOnKeyDown(tree));
    tree.__fnOnKeyDown = null;
    _fnremoveListener(tree.ownerDocument, "onselectstart", startselecting(tree));
    tree.__startselecting = null;
    
    tree.__getToggleHandler = null;
    tree.__getMouseMoveHandler = null;
    tree.__getDragStartFunction = null;
    tree.__getDragAnythingFunction = null;
    tree.__getDragStopFunction = null;
    tree.__getDragLeaveFunction = null;
    tree.__getContinueDraggingFunction = null;
    tree.__getDragEndFunction = null;
    tree.gDraggingItem = null;
    tree.gDropCurrent = null;
    tree.gDropPrevious = null;
    tree.gExprDropTarget = null;
    
    tree.__getAddItemFunction = null;
    tree.__getAddItemsFunction = null;
    tree.__getAddItemBeforeFunction = null;
    tree.__getRemoveFunction = null;
    tree.__getMoveItemFunction = null;
    tree.__getCutItemFunction = null;
    tree.__getCopyItemFunction = null;
    tree.__getPasteItemFunction = null;
    tree.__getExpandNodeFunction = null;
    tree.__getExpandAllNodesFunction = null;
    tree.__getCollapseNodeFunction = null;
    tree.__getCollapseAllNodesFunction = null;
    tree.__getHighlightFunction = null;
    tree.__getRenameFunction = null;
    tree.__getChangeIconFunction = null;
    tree.__getChangeOpenIconFunction = null;
    
    tree.__getCheckItemFunction = null;
    tree.__getIsCheckedItemFunction = null;
    tree.__getIsCheckableItemFunction = null;
    tree.__getMakeCheckableItemFunction = null;
    tree.__moveUp = null;
    tree.__moveDown = null;
    tree.__moveTop = null;
    tree.__moveEnd = null;
    tree.__moveBefore = null;
    tree.__moveAfter = null;
    tree.__getSorter = null;
    
    tree.__getSetClassFunction = null;
    tree.__hoverNode = null;
    tree.TreeSchema = null;
    if (tree.goTree) {
        tree.goTree.getTree = null;
        tree.goTree.addItem = null;
        tree.goTree.addItems = null;
        tree.goTree = null;
    }
    tree.goTreeRoot = null;
    tree.root = null;
    tree.selectedItem = null;
    tree.selectedItems = null;
    tree.currentTreeItem = null;
    tree.ItemTemplate = null;
    
    if (tree.inpField) {
        _fnremoveListener(tree.inpField, "onchange", htmlChangeListener(tree));
        tree.__htmlChangeListener = null;
        _fnremoveListener(tree.inpField, "onblur", leaveInputField(tree));
        tree.__leaveInputField = null;
    }
    tree.inpField = null;
    tree.__checkEditMode = null;
    if (activeTree == tree) 
        activeTree = null;
    if (tree.treeData) 
        tree.treeData = null;
    if (tree.treeSchema) 
        tree.treeSchema = null;
}

function createSchemaRootItem(tree, itemXML){
    var schemaRootItem = new Object();
    if (itemXML != null) {
        var xpathDescription = "./*[local-name()='description']";
        var xpathSearchPath = "./*[local-name()='searchPath']";
        var xpathDragEnabled = "./*[local-name()='dragEnabled']";
        var xpathGenericDropEnabled = "./*[local-name()='genericDropEnabled']";
        var xpathOnDrop = "./*[local-name()='ondrop']";
        var xpathOnDragenter = "./*[local-name()='ondragenter']";
        var xpathIcon = "./*[local-name()='icon']";
        var xpathOpenIcon = "*[local-name()='openIcon']";
        var xpathCheckable = "./*[local-name()='checkable']";
        var xpathCheckChildren = "./*[local-name()='checkChildren']";
        var xpathOnItemChecked = "./*[local-name()='onitemchecked']";
        var xpathOnDblclick = "*[local-name()='ondblclick']";
        var xpathOnSelect = "./*[local-name()='onselect']";
        
        if (tree.isQuirks) {
            xpathDescription = "./description";
            xpathSearchPath = "./searchPath";
            xpathDragEnabled = "./dragEnabled";
            xpathGenericDropEnabled = "./genericDropEnabled";
            xpathOnDrop = "./ondrop";
            xpathOnDragenter = "./ondragenter";
            xpathIcon = "./icon";
            xpathOpenIcon = "./openIcon";
            xpathCheckable = "./checkable";
            xpathCheckChildren = "./checkChildren";
            xpathOnItemChecked = "./onitemchecked";
            xpathOnDblclick = "./ondblclick";
            xpathOnSelect = "./onselect";
        }
        schemaRootItem.text = _fngetNodeText(itemXML, xpathDescription, ""); //NOMBV
        schemaRootItem.icons = new Array();
        schemaRootItem.icon = _fngetNodeText(itemXML, xpathIcon, IMG_ROOTICON);
        var openIcon = (schemaRootItem.icon == IMG_ROOTICON) ? IMG_OPENROOTICON : schemaRootItem.icon;
        schemaRootItem.openIcon = _fngetNodeText(itemXML, xpathOpenIcon, openIcon);
        var tupleSearch = _fngetNodeText(itemXML, xpathSearchPath, null);
        if (tupleSearch != null) 
            tree.tupleSearch = tupleSearch;
        schemaRootItem.dragEnabled = _fngetNodeText(itemXML, xpathDragEnabled, "false");
        var gDropFlag = schemaRootItem.genericDropEnabled = _fngetNodeText(itemXML, xpathGenericDropEnabled, tree.TreeSchema.genericDropEnabled);
        if (gDropFlag == "true") 
            tree.genericDropEnabled = true;
        schemaRootItem.ondragenter = _fngetNodeText(itemXML, xpathOnDragenter, null);
        schemaRootItem.ondrop = _fngetNodeText(itemXML, xpathOnDrop, null);
        
        //checkbox on treeitem
        schemaRootItem.checkable = _fngetNodeText(itemXML, xpathCheckable, "false").toLowerCase();
        schemaRootItem.checkChildren = _fngetNodeText(itemXML, xpathCheckChildren, "true").toLowerCase();
        schemaRootItem.onitemchecked = _fngetNodeText(itemXML, xpathOnItemChecked, null);
        schemaRootItem.ondblclick = _fngetNodeText(itemXML, xpathOnDblclick, null);
        schemaRootItem.onselect = _fngetNodeText(itemXML, xpathOnSelect, null);
        tree.TreeSchema.rootItem = schemaRootItem;
    }
}

function createSchemaTreeItem(tree, itemXML){
    var xpathDescription = "./*[local-name()='description']";
    var xpathSearchPath = "./*[local-name()='searchPath']";
    var xpathUpdateAllowed = "*[local-name()='updateAllowed']";
    var xpathDragEnabled = "./*[local-name()='dragEnabled']";
    var xpathGenericDropEnabled = "./*[local-name()='genericDropEnabled']";
    var xpathOnBeforeSelect = "./*[local-name()='onbeforeselect']";
    var xpathOnSelect = "./*[local-name()='onselect']";
    var xpathOnBeforeExpand = "./*[local-name()='onbeforeexpand']";
    var xpathOnExpand = "./*[local-name()='onexpand']";
    var xpathOnAfterExpand = "./*[local-name()='onafterexpand']";
    var xpathOnCopy = "./*[local-name()='oncopy']";
    var xpathOnCut = "*[local-name()='oncut']";
    var xpathOnPaste = "./*[local-name()='onpaste']";
    var xpathOnBeforeCollapse = "./*[local-name()='onbeforecollapse']";
    var xpathOnCollapse = "./*[local-name()='oncollapse']";
    var xpathOnAfterCollapse = "./*[local-name()='onaftercollapse']";
    var xpathOnChange = "*[local-name()='onchange']";
    var xpathOnDrag = "./*[local-name()='ondrag']";
    var xpathOnDrop = "./*[local-name()='ondrop']";
    var xpathOnDragenter = "./*[local-name()='ondragenter']";
    var xpathOnDragend = "./*[local-name()='ondragend']";
    var xpathDropTarget = "./*[local-name()='dropTarget']";
    var xpathIcon = "./*[local-name()='icon']";
    var xpathOpenIcon = "*[local-name()='openIcon']";
    var xpathCheckable = "./*[local-name()='checkable']";
    var xpathCheckChildren = "./*[local-name()='checkChildren']";
    var xpathOnItemChecked = "./*[local-name()='onitemchecked']";
    var xpathOnDblclick = "*[local-name()='ondblclick']";
    var xpathMultipleIcons = "./*[local-name()='icons']/*[local-name()='icon']";
    var xpathMultipleOpenIcons = "./*[local-name()='icons']/*[local-name()='openIcon']";
    var xpathContextMenu = "./*[local-name()='contextMenu']";
    
    if (tree.isQuirks) {
        xpathDescription = "./description";
        xpathSearchPath = "./searchPath";
        xpathUpdateAllowed = "./updateAllowed";
        xpathDragEnabled = "./dragEnabled";
        xpathGenericDropEnabled = "./genericDropEnabled";
        xpathOnBeforeSelect = "./onbeforeselect";
        xpathOnSelect = "./onselect";
        xpathOnBeforeExpand = "./onbeforeexpand";
        xpathOnExpand = "./onexpand";
        xpathOnAfterExpand = "./onafterexpand";
        xpathOnCopy = "./oncopy";
        xpathOnCut = "./oncut";
        xpathOnPaste = "./onpaste";
        xpathOnBeforeCollapse = "./onbeforecollapse";
        xpathOnCollapse = "./oncollapse";
        xpathOnAfterCollapse = "./onaftercollapse";
        xpathOnChange = "./onchange";
        xpathOnDrag = "./ondrag";
        xpathOnDrop = "./ondrop";
        xpathOnDragenter = "./ondragenter";
        xpathOnDragend = "./ondragend";
        xpathDropTarget = "./dropTarget";
        xpathIcon = "./icon";
        xpathOpenIcon = "./openIcon";
        xpathCheckable = "./checkable";
        xpathCheckChildren = "./checkChildren";
        xpathOnItemChecked = "./onitemchecked";
        xpathOnDblclick = "./ondblclick";
        xpathMultipleIcons = "./icons/icon";
        xpathMultipleOpenIcons = "./icons/openIcon";
        xpathContextMenu = "./contextMenu";
    }
    
    var schemaTreeItem = new Object();
    schemaTreeItem.id = itemXML.getAttribute("id");
    schemaTreeItem.text = _fngetNodeText(itemXML, xpathDescription, ""); //NOMBV
    //else alert("Description of treeItem should be defined");
    if (tmpNode = _fnselectXMLNode(itemXML, xpathSearchPath)) {
        schemaTreeItem.searchPath = _fngetTextContent(tmpNode);
        tree.TreeSchema.rootSearch = (tree.TreeSchema.rootSearch != "" ? tree.TreeSchema.rootSearch + "|" : "") + tree.tupleSearch + schemaTreeItem.searchPath;
        tree.TreeSchema.leafSearch = (tree.TreeSchema.leafSearch != "" ? tree.TreeSchema.leafSearch + "|" : "") + tree.tupleSearch + schemaTreeItem.searchPath;
        tree.TreeSchema.contSearch = (tree.TreeSchema.contSearch != "" ? tree.TreeSchema.contSearch + "|" : "") + schemaTreeItem.searchPath;
        
        var localNameRegExp = /[*][\[]local-name\(\)\s*=\s*['"](.*?)['"][\]]/g;
        var squareBracketRegexp = /^(.*)[\[](.*)[\]]$/; //check for [] at the end 
        var simpleSearchPath = schemaTreeItem.searchPath.replace(localNameRegExp, "##$1##"); //xpath without local-name
        if (simpleSearchPath.match(squareBracketRegexp)) // there is some deptsearch
        {
            var searchPath = RegExp.$1;
            var deptSearch = RegExp.$2;
            schemaTreeItem.searchPath = searchPath.replace(/##(.*?)##/g, "*[local-name()='$1']"); //xpath with local-name again
            schemaTreeItem.deptSearch = deptSearch.replace(/##(.*?)##/g, "*[local-name()='$1']");
        }
        else {
            schemaTreeItem.deptSearch = null;
        }
    }
    /*else
     {
     tree.ownerDocument.defaultView.application.showError("Search path should be defined");
     }*/
    schemaTreeItem.updateAllowed = _fngetNodeText(itemXML, xpathUpdateAllowed, tree.TreeSchema.updateAllowed);
    schemaTreeItem.dragEnabled = _fngetNodeText(itemXML, xpathDragEnabled, tree.TreeSchema.dragEnabled);
    var gDropFlag = schemaTreeItem.genericDropEnabled = _fngetNodeText(itemXML, xpathGenericDropEnabled, tree.TreeSchema.genericDropEnabled);
    if (gDropFlag == "true") 
        tree.genericDropEnabled = true;
    schemaTreeItem.onbeforeselect = _fngetNodeText(itemXML, xpathOnBeforeSelect, null);
    schemaTreeItem.onselect = _fngetNodeText(itemXML, xpathOnSelect, null);
    schemaTreeItem.onebeforeexpand = _fngetNodeText(itemXML, xpathOnBeforeExpand, null);
    schemaTreeItem.onexpand = _fngetNodeText(itemXML, xpathOnExpand, null);
    schemaTreeItem.onafterexpand = _fngetNodeText(itemXML, xpathOnAfterExpand, null);
    schemaTreeItem.oncopy = _fngetNodeText(itemXML, xpathOnCopy, null);
    schemaTreeItem.oncut = _fngetNodeText(itemXML, xpathOnCut, null);
    schemaTreeItem.onpaste = _fngetNodeText(itemXML, xpathOnPaste, null);
    schemaTreeItem.onbeforecollapse = _fngetNodeText(itemXML, xpathOnBeforeCollapse, null);
    schemaTreeItem.oncollapse = _fngetNodeText(itemXML, xpathOnCollapse, null);
    schemaTreeItem.onaftercollapse = _fngetNodeText(itemXML, xpathOnAfterCollapse, null);
    schemaTreeItem.onchange = _fngetNodeText(itemXML, xpathOnChange, null);
    schemaTreeItem.ondrag = _fngetNodeText(itemXML, xpathOnDrag, null);
    schemaTreeItem.ondrop = _fngetNodeText(itemXML, xpathOnDrop, null);
    schemaTreeItem.ondragenter = _fngetNodeText(itemXML, xpathOnDragenter, null);
    schemaTreeItem.dropTarget = _fngetNodeText(itemXML, xpathDropTarget, null);
    schemaTreeItem.icon = _fngetNodeText(itemXML, xpathIcon, IMG_FOLDERICON);
    var openIcon = (schemaTreeItem.icon == IMG_FOLDERICON) ? IMG_OPENFOLDERICON : schemaTreeItem.icon;
    schemaTreeItem.openIcon = _fngetNodeText(itemXML, xpathOpenIcon, openIcon);
    //checkbox on treeitem
    schemaTreeItem.checkable = _fngetNodeText(itemXML, xpathCheckable, "false").toLowerCase();
    schemaTreeItem.checkChildren = _fngetNodeText(itemXML, xpathCheckChildren, "true").toLowerCase();
    schemaTreeItem.onitemchecked = _fngetNodeText(itemXML, xpathOnItemChecked, null);
    schemaTreeItem.ondblclick = _fngetNodeText(itemXML, xpathOnDblclick, null);
    schemaTreeItem.icons = new Array();
    schemaTreeItem.openIcons = new Array();
    try {
        var multIcons = _fnselectXMLNodes(itemXML, xpathMultipleIcons);
        for (var i = 0; i < multIcons.length; i++) {
            schemaTreeItem.icons[schemaTreeItem.icons.length] = multIcons[i];
        }
        multIcons = _fnselectXMLNodes(itemXML, xpathMultipleOpenIcons);
        for (var i = 0; i < multIcons.length; i++) {
            schemaTreeItem.openIcons[schemaTreeItem.openIcons.length] = multIcons[i];
        }
    } 
    catch (e) {
        schemaTreeItem.icons.length = 0;
    }
    if (tmpNode = _fnselectXMLNode(itemXML, xpathContextMenu)) 
        schemaTreeItem.contextMenu = _fngetTextContent(tmpNode);
    else 
        schemaTreeItem.contextMenu = null;
    tree.TreeSchema.nodeItems[tree.TreeSchema.nodeItems.length] = schemaTreeItem;
}

Tree.prototype.addSchemaItem = function(item){
    if (typeof(item.documentElement) != "undefined") {
        item = item.documentElement;
    }
    if (item.tagName == "TreeItem") {
        createSchemaTreeItem(this, item);
        Docex.appendXMLNode(item, this.treeSchema);
    }
}
Tree.prototype.setEnabled = function (oEnabled)
{
	if (typeof(oEnabled)=="undefined") this.enabled = true;
	else if (typeof(oEnabled)=="string") this.enabled = (oEnabled.toLowerCase()=="true");
	else this.enabled = oEnabled;
	var labelNodes = this.getElementsByTagName("SPAN");
	if (this.enabled)
	{
		this.__emptyImg.style.display = "none";
		for(var i=0;i<labelNodes.length;i++)
		{
			HTMLUtil.removeClassName("disabled", labelNodes[i]);
		}
	}
	else
	{
		this.__emptyImg.style.display = "block";
		for(var i=0;i<labelNodes.length;i++)
		{
			HTMLUtil.addClassName("disabled", labelNodes[i]);
		}
		var selectedItems = this.selectedItems;
		for (var i in selectedItems)
		{
			selectedItems[i].highlight(false);
		}
	}
}
Tree.prototype.setTreeSchema = function(schema){
    var tmpNode;
    this.root = null;
    this.TreeSchema.nodeItems.length = 0;
    this.TreeSchema.rootItem = null;
    this.TreeSchema.rootSearch = this.TreeSchema.contSearch = this.TreeSchema.leafSearch = "";
    
    var xpathUseTreeLines = "./*[local-name()='useTreeLines']";
    var xpathBackground = "./*[local-name()='background']";
    var xpathUsePlusIcons = "*[local-name()='usePlusIcons']";
    var xpathSearchPath = "./*[local-name()='searchPath']";
    var xpathBuildOnExpand = "./*[local-name()='buildOnExpand']";
    var xpathGenericDropEnabled = "./*[local-name()='genericDropEnabled']";
    var xpathRoot = "//*[local-name()='Root']";
    var xpathUpdateAllowed = "./*[local-name()='updateAllowed']";
    var xpathMultipleSelect = "./*[local-name()='multipleSelect']";
    var xpathTreeItem = "*[local-name()='TreeItem']";
    if (this.isQuirks) {
        xpathUseTreeLines = "./useTreeLines";
        xpathBackground = "./background";
        xpathUsePlusIcons = "usePlusIcons";
        xpathSearchPath = "./searchPath";
        xpathBuildOnExpand = "./buildOnExpand";
        xpathGenericDropEnabled = "./genericDropEnabled";
        xpathRoot = "//Root";
        xpathUpdateAllowed = "./updateAllowed";
        xpathMultipleSelect = "./multipleSelect";
        xpathTreeItem = "TreeItem";
    }
    if (typeof(schema) == "string") {
        with (this.ownerDocument.defaultView) {
            var root = eval(schema);
            this.TreeSchema.root = root.XMLDocument ? root.XMLDocument.documentElement : root.documentElement;
        }
    }
    else {
        this.TreeSchema.root = schema;
    }
    if (_fngetNodeText(this.TreeSchema.root, xpathUseTreeLines) == "false") 
        this.TreeSchema.useTreeLines = false;
    else 
        this.TreeSchema.useTreeLines = true;
    this.TreeSchema.background = _fngetNodeText(this.TreeSchema.root, xpathBackground, this.ownerDocument.body.background);
    if (_fngetNodeText(this.TreeSchema.root, xpathUsePlusIcons) == "false") 
        this.TreeSchema.usePlusIcons = false;
    else 
        this.TreeSchema.usePlusIcons = true;
    var tupleSearch = _fngetNodeText(this.TreeSchema.root, xpathSearchPath, null);
    if (tupleSearch != null) 
        this.tupleSearch = tupleSearch;
    if (tmpNode = _fnselectXMLNode(this.TreeSchema.root, xpathBuildOnExpand)) 
        this.TreeSchema.buildOnExpand = (_fngetTextContent(tmpNode) == "false") ? false : true;
    else 
        this.TreeSchema.buildOnExpand = false;
    
    var gDropFlag = this.TreeSchema.genericDropEnabled = _fngetNodeText(this.TreeSchema.root, xpathGenericDropEnabled, false);
    if (gDropFlag == "true") 
        this.genericDropEnabled = true;
    
    tmpNode = _fnselectXMLNode(this.TreeSchema.root, xpathRoot);
    if (tmpNode) 
        createSchemaRootItem(this, tmpNode);
    if (this.BusDataIsland) {
        try {
            with (this.ownerDocument.defaultView) {
                this.TreeSchema.xmlData = eval(this.BusDataIsland + ".data");
            }
        } 
        catch (e) {
            //this.ownerDocument.defaultView.application.showError("Can't get the data from BusDataIsland.");
        }
    }
    this.TreeSchema.updateAllowed = _fngetNodeText(this.TreeSchema.root, xpathUpdateAllowed, "false");
    if (tmpNode = _fnselectXMLNode(this.TreeSchema.root, xpathMultipleSelect)) 
        this.TreeSchema.multipleSelect = (_fngetTextContent(tmpNode) == "false") ? false : true;
    else 
        this.TreeSchema.multipleSelect = false;
    
    // Handle tree items
    var nodeItems = _fnselectXMLNodes(this.TreeSchema.root, xpathTreeItem);
    for (var i = 0; i < nodeItems.length; i++) {
        var item = nodeItems[i];
        createSchemaTreeItem(this, item);
    }
    nodeItems = null;
    buildTree(this);
    this.treeSchema = this.TreeSchema.root;
}

Tree.prototype.getTreeSchema = function(){
    return this.treeSchema;
}

/*Creates a tree from an xml structure (treeData). 
 First the tree base is created, then the root and finally
 the dataNodes which are available directly under the root are rendered.*/
function buildTree(tree){
    if (!tree.TreeSchema.xmlData || tree.TreeSchema.xmlData.documentElement == null || tree.TreeSchema.rootSearch == "") {
        return;
    }
    if (tree.goTree) {
        //tree.ownerDocument.defaultView.application.addGarbage(tree.goTree);
        tree.goTree = null;
    }
    tree.goTree = createTree(tree);
    
    var parentItem = tree.goTree;
    if (tree.TreeSchema.rootItem) {
        parentItem = createRootNode(tree, parentItem, tree.TreeSchema.rootItem.text, tree.TreeSchema.rootItem); //NOMBV
    }
    //create an array of dataNodes, these can be rendered in one go
    var dataNodes = getXMLNodes(tree, tree.TreeSchema.xmlData.documentElement, tree.TreeSchema.rootSearch);
    //create the first level in the tree base, use dataNodes as base
    createTreeLevelFromDataArray(tree, dataNodes, parentItem, false);
}

function readSubTreeLevel(tree, pXML, pTree, fromLevel, toLevel){
    if (parseInt(pTree.getAttribute("level")) < fromLevel) {
        for (var treeChild = getNodeContainer(pTree).firstChild; treeChild; treeChild = treeChild.nextSibling) {
            readSubTreeLevel(tree, treeChild.data, treeChild, fromLevel, toLevel);
        }
        return;
    }
    pTree.readOnExpand = false;
    var childs = getXMLNodes(tree, pXML, tree.TreeSchema.contSearch);
    createTreeLevelFromDataArray(tree, childs, pTree, false);
    
    childs = null;
}

function startselecting(tree){
    if (!tree.__startselecting) {
        tree.__startselecting = function(eventObject){
            var elmt = _fngetEventTarget(eventObject);
            // if the element does not belong to the tree then return
            if (typeof(elmt.getTreeItem) == 'undefined') 
                return;
            //Check if the element in selection is input/textarea/contentEditable
            if (elmt.tagName.toLowerCase() != "input" && elmt.tagName.toLowerCase() != "textarea") {
                //Check the cases where html elements are present inside content editable elements. Such cases also, selection should be avoided. Check is done for 3 level depth
                if (elmt.contentEditable == "true" || (elmt.parentNode && elmt.parentNode.contentEditable == "true") || (elmt.parentNode.parentNode && elmt.parentNode.parentNode.contentEditable == "true")) 
                    return;
                eventObject.preventDefault();
            }
        }
    }
    return tree.__startselecting;
}

function checkIfNode(tree, child){
    // Check if this xml part is related to some node defined in the TreeSchema.
    if (!child) 
        return null;
    var itemType = null;
    var length = tree.TreeSchema.nodeItems.length;
    if (length == 1) 
        return tree.TreeSchema.nodeItems[0];
    for (var i = 0; i < length; i++) {
        var nodeItem = tree.TreeSchema.nodeItems[i];
        var checkName = (nodeItem.searchPath.match("/")) ? nodeItem.searchPath.substr(nodeItem.searchPath.lastIndexOf(nodeItem.searchPath.match("/")) + 1) : nodeItem.searchPath;
        if (/local-name()/.test(checkName)) {
            //TODO this must be carefull tested
            //checkName can be something like *[local-name()=='Continent'], need the part between ''
            var re = new RegExp("[\'\"](\\w+)[\'\"]");
            checkName.match(re);
            checkName = RegExp.$1;
        }
        if (checkName.substr(0, 1) == "@" && child.nodeType == 2) {
            checkName = checkName.substr(1);
        }
        var path;
        if (child.nodeType == 9) 
            child = child.documentElement;
        if (tree.isQuirks && child.ownerDocument.getProperty("SelectionLanguage") == "XSLPattern") {
            path = ".[" + nodeItem.deptSearch + "]";
        }
        else {
            path = "self::node()[" + nodeItem.deptSearch + "]";
        }
        var baseNameArray = child.nodeName.split(":"); //if data has prefix and schema has local-name
        if ((checkName == child.nodeName || checkName == "*" || checkName == baseNameArray[baseNameArray.length - 1]) && (!nodeItem.deptSearch || getXMLNode(tree, child, path))) {
            itemType = nodeItem;
            break;
        }
    }
    return itemType;
}


/*Shows the first treeItem that has the description of itemText, inSensitive (casesensitive or not)
 Do searchItemInTree, expand all levels above the node and highlight. This is a very handy function if you want to show
 a particulair node to a user.
 */
Tree.prototype.showItem = function(itemText, inSensitive, searchFullTree){
    //get the item
    var item = searchItemInTree(this, itemText, inSensitive, searchFullTree);
    //expand all the levels above item, until this (the current tree item)
    if (item != null) {
        var parentItem = item;
        do {
            parentItem = parentItem.parentNode.parentNode;
            if (parentItem) 
                parentItem.expand();
        }
        while (parentItem && parentItem != this && !parentItem.isRoot);
        
        item.select(false);
    }
    return item;
}

Tree.prototype.getItem = function(itemText, inSensitive){
    // This function searches for a treeItem given the text of that item.
    // It returns the first item found, as object of type TreeItem.
    // If not found, null is returned.
    // Since not all HTML is always available searches the tree data also for the item
    
    return searchItemInTree(this, itemText, inSensitive, true);
}

// Searches the available HTML for ALL the tree items with description itemText.
// Based on the dataNode all the HTML will be rendered, to locate all the items
// It returns all items found (optional case-insensitive), within an array as objects of type TreeItem.
Tree.prototype.getItems = function(itemText, inSensitive){
    return searchItemsInTree(this, itemText, inSensitive, true);
}

Tree.prototype.getSelectedItems = function(){
    return this.selectedItems;
}

Tree.prototype.getSelectedItem = function(){
    return this.selectedItem ? this.selectedItem.getTreeItem() : null;
}

function leafToNode(tree, item){
    item.isLeaf = false;
    setIcon(item);
    setPlusImage(tree, item);
    setContainerConnectLine(tree, item);
}

function setPlusImage(tree, node, leafToNode){
    if (node.isLeaf == true) {
    
        if (!(node.isRoot || node.isAtRootLevel)) {
            if (node.nextSibling) 
                node.className = CL_ITEMLEAF;
            else 
                node.className = CL_ITEMLEAFLAST;
            setContainerConnectLine(tree, node);
            if (!tree.TreeSchema.useTreeLines) {
                node.style.backgroundImage = "none";
            }
        }
    }
    else {
        var colClass = "";
        var expClass = "";
        
        if (!tree.TreeSchema.useTreeLines || node.isRoot || node.isAtRootLevel) {
            if (tree.TreeSchema.usePlusIcons) {
                colClass = CL_ITEMSTANDALONECOLLAPSED;
                expClass = CL_ITEMSTANDALONEEXPANDED;
            }
        }
        else {
            if (tree.TreeSchema.usePlusIcons) {
                if (node.nextSibling) {
                    colClass = CL_ITEMCOLLAPSED;
                    expClass = CL_ITEMEXPANDED;
                }
                else {
                    colClass = CL_ITEMCOLLAPSEDLAST;
                    expClass = CL_ITEMEXPANDEDLAST;
                }
            }
            var secondToLastSibling = node.parentNode.lastChild.previousSibling;
            if (secondToLastSibling) {
                var exp = new RegExp(CL_CONNECTLINE);
                if (!exp.test(secondToLastSibling.lastChild.className)) {
                    HTMLUtil.addClassName(CL_CONNECTLINE, secondToLastSibling.lastChild);
                }
            }
        }
        node.className = (node.isOpen) ? expClass : colClass;
    }
}

function setIcon(node){
    var instanceIcon = null;
    var instanceOpenIcon = null;
    if (node.data) {
        instanceIcon = _fngetNodeText(node.data, "*[local-name()='icon']", null);
        instanceOpenIcon = _fngetNodeText(node.data, "*[local-name()='openIcon']", null);
        // If an instanceIcon specified and no instanceOpenIcon.
        if (!instanceOpenIcon) 
            instanceOpenIcon = instanceIcon;
    }
    if (node.iconChanged) {
        instanceIcon = node.iconUrl;
        instanceOpenIcon = node.iconOpenUrl;
        
        // If an instanceIcon specified and no instanceOpenIcon.
        if (!instanceOpenIcon) 
            instanceOpenIcon = instanceIcon;
    }
    var itemType = node.itemType;
    if (itemType) {
        if (node.isLeaf) {
            if (instanceIcon) {
                setIconSrc(getFirstImageNode(node), instanceIcon);
            }
            else {
                var icons = itemType.icons;
                if (icons && icons.length > 0) {
                    var imgNodes = getItemImages(node);
                    for (var i = 0; i < icons.length; i++) {
                        setIconSrc(imgNodes[i], _fngetTextContent(icons[i]));
                    }
                }
                else 
                    if (itemType.icon == null || itemType.icon == IMG_FOLDERICON) {
                        setIconSrc(getFirstImageNode(node), IMG_FILEICON);
                    }
                    else {
                        setIconSrc(getFirstImageNode(node), itemType.icon);
                    }
            }
        }
        else {
            var icons = itemType.icons;
            var openIcons = itemType.openIcons;
            if (node.isOpen) {
                if (openIcons && openIcons.length > 0) {
                    var imgNodes = getItemImages(node);
                    for (var i = 0; i < openIcons.length; i++) {
                        setIconSrc(imgNodes[i], _fngetTextContent(openIcons[i]));
                    }
                }
                else {
                    setIconSrc(getFirstImageNode(node), instanceOpenIcon ? instanceOpenIcon : itemType.openIcon);
                }
            }
            else 
                if (icons && icons.length > 0) {
                    var imgNodes = getItemImages(node);
                    for (var i = 0; i < icons.length; i++) {
                        setIconSrc(imgNodes[i], _fngetTextContent(icons[i]));
                    }
                }
                else {
                    setIconSrc(getFirstImageNode(node), instanceIcon ? instanceIcon : itemType.icon);
                }
        }
    }
    else {
        if (node.isLeaf) {
            setIconSrc(getFirstImageNode(node), instanceIcon ? instanceIcon : IMG_FILEICON);
        }
        else {
            if (node.isOpen) {
                setIconSrc(getFirstImageNode(node), instanceOpenIcon ? instanceOpenIcon : IMG_OPENFOLDERICON);
            }
            else {
                setIconSrc(getFirstImageNode(node), instanceIcon ? instanceIcon : IMG_FOLDERICON);
            }
        }
    }
}

function getFirstImageNode(node){
    return getItemImages(node)[0];
}

function setIconSrc(node, src){
    var image = node.style.backgroundImage;
    var srcRoot = src.split("/");
    if (srcRoot[srcRoot.length - 1] != "none") {
        if (!image || image.indexOf(src) == -1) 
            node.style.backgroundImage = "url('" + src + "')";
    }
}

//container of the node gets a connectline, when is has children and a nextsibling (and when treelines are used)
//when the node is a lastchild it does not get a connectline
function setContainerConnectLine(tree, node){
    if (tree.TreeSchema.useTreeLines && !(node.isRoot || node.isAtRootLevel)) {
        if (node.getChildContainer == null) 
            attachEventsMethodsPropertiesSingleNode(tree, node, node.data, false, null, true);
        //if(node.getChildContainer == null) node.getChildContainer = getChildContainer;
        var container = node.getChildContainer();
        var exp = new RegExp(CL_CONNECTLINE);
        if (node.parentNode.lastChild.previousSibling && node.nextSibling) {
            if (!exp.test(container.className)) {
                HTMLUtil.addClassName(CL_CONNECTLINE, container);
            }
        }
        else {
            //no connectline required remove if present
            if (exp.test(container.className)) {
                container.className = container.className.replace(exp, "");
            }
        }
    }
}

function setEvents(tree, node){
    //expand and collapse of nodes
    var anchor = node.firstChild;
    anchor.draggable = 'true';
    HTMLUtil.addClassName("draggable", anchor);
    anchor.ondblclick = getToggleHandler(tree);
    node.onclick = getToggleHandler(tree);
    
    //select, highlight a node
    _fnaddListener(anchor, "onclick", getSelectHandler(tree));
    
    node.getLabel().onmousemove = getMouseMoveHandler(tree);
    var imageNodes = getItemImages(node);
    for (var i = 0; i < imageNodes.length; i++) {
        imageNodes[i].onmousemove = getMouseMoveHandler(tree);
    }
    _fnaddListener(node.lastChild, "ondblclick", cancelEvent(tree));
    var itemType = node.itemType;
    _fnaddListener(anchor, "ondragstart", (itemType && itemType.dragEnabled == "true") ? getDragStartFunction(tree) : getDragAnythingFunction(tree));
    _fnaddListener(node, "ondrop", getDragStopFunction(tree));
    _fnaddListener(node, "ondragenter", getContinueDraggingFunction(tree));
    _fnaddListener(node, "ondragleave", getDragLeaveFunction(tree));
    _fnaddListener(node, "ondragover", getContinueDraggingFunction(tree));
    _fnaddListener(node, "ondragend", getDragEndFunction(tree));
    
}

function removeEvents(tree, node){
    _fnremoveListener(node.firstChild, "onclick", getSelectHandler(tree));
    _fnremoveListener(node.lastChild, "ondblclick", cancelEvent(tree));
    if (node.itemType && node.itemType.contextMenu) {
        _fnremoveListener(node, "oncontextmenu", getSelectHandler(tree));
    }
    else {
        _fnremoveListener(node, "oncontextmenu", cancelEvent(tree));
    }
    node.firstChild.ondblclick = null;
    node.onclick = null;
    node.getLabel().onmousemove = null;
    var imageNodes = getItemImages(node);
    for (var i = 0; i < imageNodes.length; i++) {
        imageNodes[i].onmousemove = null;
    }
    var itemType = node.itemType;
    _fnremoveListener(node.firstChild, "ondragstart", (itemType && itemType.dragEnabled == "true") ? getDragStartFunction(tree) : getDragAnythingFunction(tree));
    _fnremoveListener(node, "ondrop", getDragStopFunction(tree));
    _fnremoveListener(node, "ondragenter", getContinueDraggingFunction(tree));
    _fnremoveListener(node, "ondragleave", getDragLeaveFunction(tree));
    _fnremoveListener(node, "ondragover", getContinueDraggingFunction(tree));
    _fnremoveListener(node, "ondragend", getDragEndFunction(tree));
}

function getMouseMoveHandler(tree){
    if (!tree.__getMouseMoveHandler) {
        tree.__getMouseMoveHandler = function(){
            var dragElement = this.getTreeItem();
            if (window.ActiveXObject && dragElement.itemType && tree.ownerDocument.defaultView.event.button == 1) {
                if (dragElement.itemType.dragEnabled == "true") {
                    dragElement.ondragstart = getDragStartFunction(tree);
                }
                else 
                    if (dragElement.itemType.dragEnabled == "false") {
                        dragElement.ondragstart = getDragAnythingFunction(tree);
                        
                    }
                dragElement.dragDrop();
            }
        }
    }
    return tree.__getMouseMoveHandler;
}

function setMethods(tree, node){
    node.addItem = getAddItemFunction(tree);
    node.addItems = getAddItemsFunction(tree);
    node.addItemBefore = getAddItemBeforeFunction(tree);
    node.remove = getRemoveFunction(tree);
    node.moveTo = getMoveItemFunction(tree);
    node.cut = getCutItemFunction(tree);
    node.copy = getCopyItemFunction(tree);
    node.paste = getPasteItemFunction(tree);
    node.expand = getExpandNodeFunction(tree);
    node.expandAll = getExpandAllNodesFunction(tree);
    node.collapse = getCollapseNodeFunction(tree)
    node.collapseAll = getCollapseAllNodesFunction(tree);
    node.select = getSelectFunction(tree);
    node.highlight = getHighlightFunction(tree);
    node.rename = getRenameFunction(tree);
    node.changeIcon = getChangeIconFunction(tree);
    node.changeOpenIcon = getChangeOpenIconFunction(tree);
    //node check
    node.check = getCheckItemFunction(tree);
    node.isChecked = getIsCheckedItemFunction(tree);
    node.makeCheckable = getMakeCheckableItemFunction(tree);
    node.isCheckable = getIsCheckableItemFunction(tree);
    
    node.setClass = getSetClassFunction(tree);
    
    //Providing the oncontextmenu directly overrides the already set oncontextmenu handler. So, doing an attachEvent
    _fnaddListener(node, "oncontextmenu", checkEditMode(tree));
    //Sorting
    node.sort = getSorter(tree);
    node.moveUp = getMoveUpFunction(tree);
    node.moveDown = getMoveDownFunction(tree);
    node.moveTop = getMoveTopFunction(tree);
    node.moveEnd = getMoveEndFunction(tree);
    node.moveBefore = getMoveBeforeFunction(tree);
    node.moveAfter = getMoveAfterFunction(tree);
}

function drawTreeNode(tree, node){
    setPlusImage(tree, node);
    setContainerConnectLine(tree, node)
}

function getMoveUpFunction(tree){
    if (!tree.__moveUp) {
        tree.__moveUp = function(){
        
            var selectedNode = this.getTreeItem();
            var prevNode = selectedNode.previousSibling;
            if (!prevNode) 
                return false;
            var node = selectedNode.parentNode.insertBefore(selectedNode, prevNode);
            drawTreeNode(tree, node);
            //if after moving up, the next node becomes the last node then reset it's CSS
            if (!selectedNode.nextSibling.nextSibling) {
                drawTreeNode(tree, selectedNode.nextSibling);
            }
            return true;
        }
    }
    return tree.__moveUp
}

function getMoveDownFunction(tree){
    if (!tree.__moveDown) {
        tree.__moveDown = function(){
            var selectedNode = this.getTreeItem();
            var nextNode = selectedNode.nextSibling;
            if (!nextNode) 
                return false;
            var node;
            if (!nextNode.nextSibling) {
                //if before moving the node it is at 2nd last position
                node = selectedNode.parentNode.appendChild(selectedNode);
                drawTreeNode(tree, node);
                //the last node now becomes the 2nd last so reset it's CSS
                drawTreeNode(tree, node.previousSibling);
                
            }
            else {
                node = selectedNode.parentNode.insertBefore(selectedNode, nextNode.nextSibling);
                drawTreeNode(tree, node);
            }
            return true;
        }
    }
    return tree.__moveDown
}

function getMoveTopFunction(tree){
    if (!tree.__moveTop) {
        tree.__moveTop = function(){
            var selectedNode = this.getTreeItem();
            var firstNode = selectedNode.parentNode.firstChild;
            if (firstNode == selectedNode) 
                return false;
            var lastnode = false;
            if (!selectedNode.nextSibling) {
                lastnode = true;
            }
            selectedNode.parentNode.insertBefore(selectedNode, firstNode);
            drawTreeNode(tree, selectedNode);
            if (lastnode) 
                drawTreeNode(tree, selectedNode.parentNode.lastChild);
            
            return true;
        }
    }
    return tree.__moveTop;
}

function getMoveEndFunction(tree){
    if (!tree.__moveEnd) {
        tree.__moveEnd = function(){
        
            var selectedNode = this.getTreeItem();
            var lastNode = selectedNode.parentNode.lastChild;
            if (lastNode == selectedNode) 
                return false;
            var lastNode = selectedNode.parentNode.appendChild(selectedNode);
            drawTreeNode(tree, lastNode);
            //The 2nd last now becomes the last 
            drawTreeNode(tree, lastNode.previousSibling);
            return true;
        }
    }
    return tree.__moveEnd;
}

function getMoveBeforeFunction(tree){
    if (!tree.__moveBefore) {
        tree.__moveBefore = function(otherTreeNode){
            var currentNode = this.getTreeItem();
            
            if (!otherTreeNode) 
                return false;
            if (otherTreeNode && (otherTreeNode != currentNode) && (otherTreeNode != currentNode.nextSibling)) {
                var lastnode = false;
                if (!currentNode.nextSibling) {
                    lastnode = true;
                }
                currentNode.parentNode.insertBefore(currentNode, otherTreeNode);
                drawTreeNode(tree, currentNode);
                if (lastnode) 
                    drawTreeNode(tree, currentNode.parentNode.lastChild);
                return true;
            }
            return false;
        }
    }
    return tree.__moveBefore;
}

function getMoveAfterFunction(tree){
    if (!tree.__moveAfter) {
        tree.__moveAfter = function(otherTreeNode){
            var currentNode = this.getTreeItem();
            
            if (!otherTreeNode) 
                return false;
            if (otherTreeNode && (otherTreeNode != currentNode) && (currentNode != otherTreeNode.nextSibling)) {
                var afterOtherTreeNode = otherTreeNode.nextSibling;
                var lastnode = false;
                if (!currentNode.nextSibling) {
                    lastnode = true;
                }
                if (afterOtherTreeNode) {
                    currentNode.parentNode.insertBefore(currentNode, afterOtherTreeNode);
                    drawTreeNode(tree, currentNode);
                    if (lastnode) 
                        drawTreeNode(tree, currentNode.parentNode.lastChild);
                    return true;
                }
                else {
                    var lastTreeNode = currentNode.parentNode.lastChild;
                    if (lastTreeNode != currentNode) {
                        currentNode.moveEnd();
                        return true;
                    }
                }
            }
            return false;
        }
    }
    return tree.__moveAfter;
}

function getSorter(tree){
    if (!tree.__getSorter) {
        tree.__getSorter = function(bAscending, sortAlgorithm){
            var selectedNode = this.getTreeItem();
            selectedNode.__sort = new Array();
            if (selectedNode.__sort["status"] == "inprogress") 
                return;
            
            selectedNode.__sort["ascending"] = bAscending ? bAscending : false;
            selectedNode.__sort["status"] = "inprogress";
            selectedNode.__sort["customAlg"] = false;
            var sortedDataNodes = new Array();
            if (sortAlgorithm) {
                selectedNode.__sort["customAlg"] = true;
                with (tree.ownerDocument.defaultView) {
                    sortedDataNodes = eval(sortAlgorithm)(selectedNode.__sort["ascending"]);
                }
                selectedNode.addItems(sortedDataNodes, selectedNode);
            }
            else 
                executeDefaultSort(selectedNode);
            selectedNode.__sort["status"] = "done";
        }
    }
    return tree.__getSorter;
}

function executeDefaultSort(node){
    var nodesToSort = new Array();
    var sortedFolderArray = new Array();
    var sortedLeafArray = new Array();
    
    var childs = node.getChildContainer().childNodes;
    for (var i = 0; i < childs.length; i++) {
        nodesToSort[i] = _fngetTextContent(childs[i].getLabel());
    }
    nodesToSort = nodesToSort.sort();
    
    for (var i = 0; i < nodesToSort.length; i++) {
        var item = node.getItem(nodesToSort[i], false);
        var itemToAdd = item.data.cloneNode(true);
        // Depending on whether the item is a leaf or folder push them into the respective array.
        if (item.isLeaf) 
            sortedLeafArray.push(itemToAdd);
        else 
            sortedFolderArray.push(itemToAdd);
        item.remove();
    }
    if (!node.__sort["ascending"]) {
        sortedFolderArray.reverse();
        sortedLeafArray.reverse();
    }
    node.addItems(sortedFolderArray, node);
    node.addItems(sortedLeafArray, node);
}

function removeMethods(tree, node, toCut){
    node.addItem = null;
    node.addItems = null;
    node.showItem = null;
    node.addItemBefore = null;
    node.remove = null;
    node.moveTo = null;
    node.cut = null;
    node.copy = null;
    node.paste = null;
    node.expand = null;
    node.expandAll = null;
    node.collapse = null;
    node.collapseAll = null;
    node.select = null;
    node.highlight = null;
    node.rename = null;
    node.changeIcon = null;
    node.changeOpenIcon = null;
    node.sort = null;
    node.check = null;
    node.isChecked = null;
    node.makeCheckable = null;
    node.isCheckable = null;
    node.getCheckableItems = null;
    node.setClass = null;
    
    node.moveUp = null;
    node.moveDown = null;
    node.moveTop = null;
    node.moveEnd = null;
    node.moveBefore = null;
    node.moveAfter = null;
    _fnremoveListener(node, "oncontextmenu", checkEditMode(tree));
    if (!toCut) {
        node.getChildContainer = null;
        node.getIcon = null;
        node.getItem = null;
        node.getItems = null;
        node.getLabel = null;
        node.getParentItem = null;
        //node.getPlus = null;
        node.getTree = null;
        node.getTreeItem = null;
        node.setLabel = null;
        node.rename = null;
        node.select = null;
    }
}

/* Sets an extra class to a treeItem, e.g. to make it stand out.
 To remove the class do treeItem.setClass("");
 */
function getSetClassFunction(tree){
    if (!tree.__getSetClassFunction) {
        tree.__getSetClassFunction = function(classStr){
            if (classStr == null) 
                classStr = "";
            var labelNode = this.firstChild.lastChild;
            if (labelNode.customClass) {
                //remove current customClass
                HTMLUtil.removeClassName(labelNode.customClass, labelNode);
            }
            labelNode.customClass = classStr;
            HTMLUtil.addClassName(classStr, labelNode);
        }
    }
    return tree.__getSetClassFunction;
}

/*handles the (un)checking of a tree item. When an uncheckable treeitem is checked it can still pass the checked
 status to possible checkable children.
 bSelect : boolean, required, check or uncheck the tree item
 bPassCheck: boolean, optional, will all checkable children get the same checked status. By default the property
 checkChildren determines what happens.
 */
function getCheckItemFunction(tree){
    if (!tree.__getCheckItemFunction) {
        tree.__getCheckItemFunction = function(bSelect, bPassCheck){
            if (bSelect != true && bSelect != false) 
                return;
            if (bPassCheck || (bPassCheck == null && this.itemType.checkChildren == "true")) {
                //this node and all children checkboxes must be updated
                updateAllCheckboxes(tree, this, bSelect);
            }
            else {
                //only this item must be updated
                updateCheckbox(tree, this, bSelect);
            }
        }
    }
    return tree.__getCheckItemFunction;
}

//treeitem and its children are (un)checked recursively
function updateAllCheckboxes(tree, treeItem, bSelect){
    updateCheckbox(tree, treeItem, bSelect);
    var container = treeItem.getChildContainer();
    for (var child = container.firstChild; child; child = child.nextSibling) {
        updateAllCheckboxes(tree, child, bSelect)
    }
}

//updates the status of the checkbox of a treeitem
function updateCheckbox(tree, treeItem, bSelect){
    var chkbox = getCheckboxElement(treeItem);
    if (chkbox == null) 
        return;
    chkbox.checked = bSelect ? true : false;
    
    fireOncheckEvent(tree, treeItem, chkbox);
}

//returns the checkbox element for a tree item
function getCheckboxElement(node){
    var chkbox = node.firstChild.firstChild;
    if (chkbox.type && chkbox.type.toLowerCase() == "checkbox") {
        return chkbox;
    }
}

//returns the check status of a tree item, returns null when there is no checkbox
function getIsCheckedItemFunction(tree){
    if (!tree.__getIsCheckedItemFunction) {
        tree.__getIsCheckedItemFunction = function(){
            var chkbox = getCheckboxElement(this);
            if (chkbox == null) 
                return null;
            return chkbox.checked;
        }
    }
    return tree.__getIsCheckedItemFunction;
}

//makes a treeItem checkable (add a checkbox)
function getMakeCheckableItemFunction(tree){
    if (!tree.__getMakeCheckableItemFunction) {
        tree.__getMakeCheckableItemFunction = function(bSelect){
            var chkbox = getCheckboxElement(this);
            if (chkbox && !bSelect) {
                //remove the checkbox
                chkbox.getTreeItem = null;
                //tree.ownerDocument.defaultView.application.addGarbage(chkbox);
                return;
            }
            if (!chkbox && bSelect) {
                //add a checkbox
                var firstImg = getFirstImageNode(this);
                var str = "<input class='" + CL_ITEMCHECKBOX + "' type='checkbox' />";
                insertHTML(firstImg, "beforeBegin", str);
                getCheckboxElement(this).getTreeItem = getTreeItem;
            }
        }
    }
    return tree.__getMakeCheckableItemFunction;
}

//returns true when the treeItem is checkable (has a checkbox)
function getIsCheckableItemFunction(tree){
    if (!tree.__getIsCheckableItemFunction) {
        tree.__getIsCheckableItemFunction = function(){
            var chkbox = getCheckboxElement(this);
            if (chkbox) 
                return true;
            return false;
        }
    }
    return tree.__getIsCheckableItemFunction;
}

/* Returns an array with the treeItems which have checked=bSelect.
 treeItems which are not checkable are never returned
 */
Tree.prototype.getCheckableItems = function(bSelect){
    //all nodes under the treeItem have to be rendered to check the status
    if (bSelect != true && bSelect != false) 
        return null;
    var tree = getTreeFromTreeNode(this);
    if (this == tree) {
        renderSubTree(tree.root);
    }
    else {
        renderSubTree(this);
    }
    var checkboxNodes = this.getElementsByTagName("INPUT");
    var checkItems = new Array();
    
    var length = checkboxNodes.length;
    var chkbox;
    for (var i = 0; i < length; i++) {
        chkbox = checkboxNodes[i];
        if (chkbox.checked == bSelect) {
            checkItems.push(chkbox.getTreeItem());
        }
    }
    return checkItems;
}

function checkEditMode(tree){
    if (!tree.__checkEditMode) {
        tree.__checkEditMode = function(){
            if (tree.inpField && tree.inpField.style.display == "") 
                tree.inpField.blur();
        }
    }
    return tree.__checkEditMode;
}

function createTree(tree){
    gDocument = tree.ownerDocument;
    var emptyImage = "<img class='emptyimage' ondragstart='return false;' xsrc='/cordys/wcp/theme/default/icon/interface/blank.png'/>";
    if (tree.leftFolding) {
        tree.innerHTML = "<div class='" + CL_CONTAINER_LEFTFOLDING + "' level='1'><ul></ul>" + emptyImage + "</div>";
    }
    else {
        tree.innerHTML = "<div class='" + CL_CONTAINER_RIGHTFOLDING + "' level='1'><ul></ul>" + emptyImage + "</div>";
    }
    tree.__emptyImg = tree.getElementsByTagName("IMG")[0];
    var treeRoot = tree.firstChild;
    
    if (tree.width) 
        treeRoot.style.width = tree.width;
    if (tree.height) 
        treeRoot.style.height = tree.height;
    if (tree.TreeSchema.background) 
        treeRoot.style.background = tree.TreeSchema.background;
    
    treeRoot.getChildContainer = getChildContainer;
    treeRoot.isTree = true;
    treeRoot.getTree = getTreeFunction(tree);
    treeRoot.addItem = getAddItemFunction(tree);
    treeRoot.addItems = getAddItemsFunction(tree);
    treeRoot.onmousemove = handleOnmousemove(tree);
    treeRoot.onmouseout = handleOnmouseout(tree);
    return treeRoot;
}

function handleOnmousemove(tree){
    return function(eventObject){
        if (!eventObject) {
            eventObject = tree.ownerDocument.defaultView.event;
        }
        var node = _fngetEventTarget(eventObject);
        
        if (!node || node.nodeType != 1) 
            return;
        
        var nodeTagName = node.tagName.toLowerCase();
        if (nodeTagName == "li") {
        
            if (isPlusMinHovered(tree, eventObject, node)) {
                HTMLUtil.addClassName(CL_HOVER_PLUSMIN, node);
            }
            else {
                HTMLUtil.removeClassName(CL_HOVER_PLUSMIN, node);
            }
            return;
        }
        if (nodeTagName == "a") {
            node = node.lastChild;
        }
        if (!node || node.nodeType != 1) 
            return;
        
        var aNode = node.parentNode;
        if (aNode.tagName.toLowerCase() == "div") 
            aNode = aNode.parentNode;
        if (aNode.tagName.toLowerCase() != "a") 
            return;
        
        if (tree.__hoverNode && tree.__hoverNode != aNode) {
            //remove hover class from current node
            HTMLUtil.removeClassName(CL_HOVER, tree.__hoverNode);
        }
        if (!HTMLUtil.hasClassName(CL_HOVER, aNode)) {
            HTMLUtil.addClassName(CL_HOVER, aNode);
        }
        tree.__hoverNode = aNode;
    }
}

/*returns true when the mouse is over the backgroundimage of the <li>=treeItem and it is not a leaf
 so the backgroundimage is either a plus or a minus.
 */
function isPlusMinHovered(tree, eventObject, node){
    if (node.isLeaf) 
        return false;
    if (_fngetEventTarget(eventObject).tagName.toLowerCase() != "li") 
        return false;
    var x, y;
    if (window.ActiveXObject) {
        y = eventObject.y; //NOMBV
        if (tree.isQuirks || tree.leftFolding) {
            x = eventObject.x; //NOMBV
        }
        else {
            x = eventObject.x - (parseInt(node.getAttribute("level")) - 2) * node.offsetLeft; //NOMBV
        }
    }
    else {
        x = eventObject.layerX; //NOMBV
        y = eventObject.layerY; //NOMBV
    }
    if (tree.leftFolding) {
        var distRight = node.offsetWidth - x; //distance from right edge of li (over background image)
        return distRight < MOVE_HAND_MARGIN_X && distRight > 0 && y < MOVE_HAND_MARGIN_Y;
    }
    return (x < MOVE_HAND_MARGIN_X && y < MOVE_HAND_MARGIN_Y);
}

function handleOnmouseout(tree){
    return function(){
        if (tree.__hoverNode) {
            HTMLUtil.removeClassName(CL_HOVER, tree.__hoverNode);
            HTMLUtil.removeClassName(CL_HOVER_PLUSMIN, tree.__hoverNode.parentNode);
        }
    }
}

function getChildContainer(){
    var container;
    if (this.isTree) 
        container = this.firstChild;
    //if (this.nodeName=="tree" || this.ownerDocument.defaultView.application.hasType(this, TREE_LIBRARY)) container = this.firstChild.firstChild;
    if (this.getAttribute("type") == "tree") 
        container = this.firstChild.firstChild;
    else 
        container = this.lastChild;
    
    if (!container.firstChild && !this.childrenRendered && this.data && !this.isOpen && !this.isLeaf) {
        //the existing children of this treeitem have not been rendered
        //do so before the container is made available in application
        createTreeLevel(getTreeFromTreeNode(this), this.data, this);
    }
    return container;
}

// For internal usage, same as getChildContainer, but without rendering the node if not already done.
function getNodeContainer(node){
    if (node.isTree) 
        return node.firstChild;
    //if (node.nodeName == "tree" || node.ownerDocument.defaultView.application.hasType(node, TREE_LIBRARY)) return node.firstChild.firstChild;
    if (node.getAttribute("type") == "tree") 
        return node.firstChild.firstChild;
    else 
        return node.lastChild;
}

function getTreeFunction(tree){
    return function(){
        return tree;
    }
}

function getTreeItem(){
    var item = this;
    while (item) {
        if (item.isLeaf == false || item.isLeaf == true) 
            return item;
        item = item.parentNode;
    }
}

function getIcon(){
    return getFirstImageNode(this);
}

function getLabel(){
    return this.firstChild.lastChild;
}

function getParent(){
    var item = this.parentNode;
    while (item) {
        if (item.isLeaf == false || item.isLeaf == true) 
            return item;
        item = item.parentNode;
    }
}

//handles the expand and collapse of a node
function getToggleHandler(tree){
    if (!tree.__getToggleHandler) {
        tree.__getToggleHandler = function(eventObject){
            if (!eventObject) {
                eventObject = tree.ownerDocument.defaultView.event;
            }
            eventObject.continueBubbling = true;
            eventObject.stopPropagation();
            
            var treeItem = getListNode(this.getTreeItem());
            var scrollRight;
            if (tree.leftFolding) {
                scrollRight = tree.firstChild.offsetWidth - tree.clientWidth - tree.scrollLeft;
                scrollRight = scrollRight < 0 ? 0 : scrollRight;
            }
            // Global variable indicating whether to show a textbox for rename or not
            if (eventObject.type == "dblclick") {
                tree.gbDoubleClick = true;
                fireOndblclickEvent(tree, treeItem);
                if (treeItem.__cancelExpandCollapse) 
                    return;
                treeItem.__cancelExpandCollapse = false;
            }
            else {
                if (!isPlusMinHovered(tree, eventObject, treeItem)) 
                    return;
                //onclick was on <li>, but not on the background-image (plus/min icon)
            }
            if (treeItem.isLeaf == false) {
                if (treeItem.isOpen) {
                    treeItem.collapse();
                }
                else {
                    treeItem.expand();
                }
            }
            if (tree.leftFolding) {
                tree.scrollLeft = tree.firstChild.offsetWidth - tree.clientWidth - scrollRight;
            }
        }
    }
    return tree.__getToggleHandler;
}

// Expands a particular node in the tree. When the children of the node have not yet been rendered, then the 
// html has to be created for this node. 
function getExpandNodeFunction(tree){
    if (!tree.__getExpandNodeFunction) {
        tree.__getExpandNodeFunction = function(eventObject){
            var newSubTree = null;
            var treeItem = getListNode(this);
            if (!tree.getSelectedItem()) {
                if (!eventObject) 
                    eventObject = tree.ownerDocument.defaultView.event;
                addToSelected(tree, treeItem, eventObject);
            }
            if (this.readOnExpand) {
                readSubTreeLevel(tree, this.data, this, parseInt(this.getAttribute("level")) + 1, parseInt(this.getAttribute("level")) + 2);
                this.readOnExpand = false;
            }
            if (tree.sortOnExpand) {
                treeItem.sort(tree.sortOnExpand.toLowerCase() == "ascending", tree.sortAlgorithm);
                
            }
            if (this.itemType.onbeforeexpand && this.expanding != true) {
                this.expanding = true;
                with (tree.ownerDocument.defaultView) {
                    newSubTree = eval(this.itemType.onbeforeexpand)(this);
                }
                this.expanding = false;
                
                if (typeof(newSubTree) != "undefined" && newSubTree != null) {
                    if (typeof(newSubTree) == "boolean" && newSubTree == false) 
                        return;
                }
            }
            if (this.itemType.onexpand && this.expanding != true) {
                this.expanding = true;
                with (tree.ownerDocument.defaultView) {
                    newSubTree = eval(this.itemType.onexpand)(this);
                }
                this.expanding = false;
                
                if (typeof(newSubTree) != "undefined" && newSubTree != null) {
                    if (typeof(newSubTree) == "boolean" && newSubTree == false) 
                        return;
                    var dataNodes = getXMLNodes(tree, newSubTree, tree.TreeSchema.rootSearch);
                    
                    if (dataNodes.length == 0) {
                        dataNodes = getXMLNodes(tree, newSubTree, tree.TreeSchema.leafSearch);
                    }
                    createTreeLevelFromDataArray(tree, dataNodes, this, false);
                }
            }
            this.isOpen = true;
            var container = getNodeContainer(this);
            if (container.childNodes.length > 0) {
                // there are already children available, the container has innerHTML, so this
                expandNode(this, tree);
                scrollIntoView(tree, this, true);
            }
            else {
                // create the HTML for the children
                if (!this.isLeaf) {
                    //check whether the node has been already rendered, if so do not render
                    //It can be that a node has no children, but has been rendered
                    //e.g. when the last child has been removed.
                    if (!this.childrenRendered) {
                        //children have not yet been rendered -> render
                        if (this.data != null) {
                            createTreeLevel(tree, this.data, this);
                        }
                    }
                    if (container.firstChild) {
                        expandNode(this, tree);
                    }
                    else {
                        collapseNode(this, tree);
                    }
                    setPlusImage(tree, this);
                }
            }
            setContainerConnectLine(tree, this);
            //if (this.minusIcon && !this.isLeaf)	this.firstChild.src = this.minusIcon;			
            setIcon(this);
            if (this.itemType.onafterexpand && this.expanding != true) {
                with (tree.ownerDocument.defaultView) {
                    eval(this.itemType.onafterexpand)(this);
                }
            }
        }
    }
    return tree.__getExpandNodeFunction;
}

/*
 bChildrenInView = true ->
 Scroll the treeItem with children into view, as much as possible
 1. More children than can be shown at once, scroll treeItem to top and show as many children as possible
 2. Tree item is higher than visible, scroll up, such that treeItem is the first visble (like 1.)
 3. Enough room to shown treeItem + children and lastChild is not visible. Scroll down suchthat lastchild is just visible
 bChildrenInView = false ->
 relevant while navigating with keypresses. Scroll treeItem into view:
 arrow up: if treeItem not visible, scroll suchthat treeItem is first visible
 arrow down: if treeItem not visible, scroll suchthat treeItem is last visible
 */
function scrollIntoView(tree, treeItem, bChildrenInView){
    //container scrolls inside the tree node
    var container = tree.firstChild;
    var iOffsetTop = 0;
    var node = treeItem;
    //determine the offsetTop for treeItem to container
    while (node && node != container) {
        iOffsetTop += node.offsetTop;
        node = node.offsetParent;
    }
    var tScrollTop = tree.scrollTop;
    var tClientHeight = tree.clientHeight;
    if (bChildrenInView) {
        if (treeItem.offsetHeight > tClientHeight || iOffsetTop < tScrollTop) {
            // If the treeItem with its children doesn't fit in the scrollwindow, view the treeItem at the top of the scrollwindow
            // or treeItem is not visible and above the parent window
            tree.scrollTop = iOffsetTop;
            return;
        }
        var bottomLastChild = iOffsetTop + treeItem.offsetHeight;
        if (bottomLastChild > (tScrollTop + tClientHeight)) {
            //treeitem+children are too low. Scroll into view such that lastChild is just visible
            tree.scrollTop = bottomLastChild - tClientHeight;
        }
        return;
    }
    else {
        if (iOffsetTop < tScrollTop) {
            //scrollup such that treeItem is the first visible
            tree.scrollTop = iOffsetTop;
            return;
        }
        var iHeight = treeItem.firstChild.offsetHeight;
        if ((iOffsetTop + iHeight) > (tScrollTop + tClientHeight)) {
            //scroll down such that treeItem is last visible
            if (tree.leftFolding || !tree.isQuirks) {
                tree.scrollTop = iOffsetTop + iHeight - tClientHeight + 5;
            }
            else {
                tree.scrollTop = iOffsetTop + iHeight - tClientHeight;
            }
            return;
        }
    }
}

function getCollapseNodeFunction(tree){
    if (!tree.__getCollapseNodeFunction) {
        tree.__getCollapseNodeFunction = function(){
            //If it is a leaf then no need to collapse it, so return
            if (this.isLeaf == true) 
                return;
            if (this.itemType.onbeforecollapse && this.collapsing != true) {
                this.collapsing = true;
                with (tree.ownerDocument.defaultView) {
                    if (eval(this.itemType.onbeforecollapse)(this) == false) {
                        this.collapsing = false;
                        return;
                    }
                }
                this.collapsing = false;
            }
            if (this.itemType.oncollapse && this.collapsing != true) {
                this.collapsing = true;
                with (tree.ownerDocument.defaultView) {
                    if (eval(this.itemType.oncollapse)(this) == false) {
                        this.collapsing = false;
                        return;
                    }
                }
                this.collapsing = false;
            }
            this.isOpen = false;
            collapseNode(this, tree);
            if (this.itemType.onaftercollapse && this.collapsing != true) {
                with (tree.ownerDocument.defaultView) {
                    eval(this.itemType.onaftercollapse)(this);
                }
            }
        }
    }
    return tree.__getCollapseNodeFunction;
}
//this method remebers the expanded/collapsed state of tree
function saveNavigationPath(tree, treeItem, expanded){
    var path = tree.navigationPath;
    var parentItem = treeItem.getParentItem();
    var desc = _fngetTextContent(treeItem.getLabel());
    var parentLevel = parentItem ? parentItem.getAttribute("level") : 1;
    var regex = new RegExp("(" + parentLevel + "--)(.*?)##", "i");
    var regexforrecentitem = /lastExpandedItem:(.*?)##/i;
    if (regex.test(path)) //some sibling of the expanded node are already expanded or calling expand on same expanded node
    {
        var prefix = RegExp.$1
        var allsiblings = RegExp.$2;
        var alreadyExisting = (allsiblings.indexOf(desc) >= 0);
        if (expanded == "false") {
            if (alreadyExisting) {
                path = path.replace(regex, prefix + allsiblings.replace(desc + "||", '') + "##");
                path = path.replace(/\d--##/, "");
            }
        }
        else 
            if (expanded == "true") {
                if (!alreadyExisting) {
                    path = path.replace(regex, prefix + allsiblings + desc + "||##");
                }
            }
    }
    else //the expanded node is the first expanded node of its parent
    {
        path += (parentLevel + "--" + desc + "||##");
    }
    path = path.replace(regexforrecentitem, "lastExpandedItem:" + parentLevel + "," + desc + "##");
    tree.navigationPath = path;
}

function collapseNode(node, tree){
    collapseContainer(getNodeContainer(node));
    var exp = new RegExp(EXPANDED);
    node.className = node.className.replace(exp, COLLAPSED);
    setIcon(node);
    if (tree.saveState) 
        saveNavigationPath(tree, node, "false");
}

function expandNode(node, tree){
    expandContainer(getNodeContainer(node));
    var exp = new RegExp(COLLAPSED);
    node.className = node.className.replace(exp, EXPANDED);
    setIcon(node);
    if (tree.saveState) 
        saveNavigationPath(tree, node, "true");
}

function getListNode(treeItem){
    while (treeItem && treeItem.tagName.toLowerCase() != "li") {
        treeItem = treeItem.parentNode;
    }
    return treeItem;
}

//handles the events that cause a select (onclick on treeitem, contextmenu)
function getSelectHandler(tree){
    return function(eventObject){
        executeSelect(tree, Docex.getEventTarget(eventObject), eventObject, (eventObject.type == "contextmenu"));
    }
}

//handles the treeItem.select method
function getSelectFunction(tree){
    return function(skipSelect){
        executeSelect(tree, this, null, skipSelect);
        moveFocus(tree)(null, true);
    }
}

function executeSelect(tree, treeItem, eventObject, skipSelect){
    var treeItem = getListNode(treeItem);
    var currentTreeItem = tree.currentTreeItem = treeItem.getTreeItem();
    var selectedItem = tree.selectedItem;
    
    if (!skipSelect && currentTreeItem.itemType.onbeforeselect) {
        with (tree.ownerDocument.defaultView) {
            var bBeforeSelect = eval(currentTreeItem.itemType.onbeforeselect)(currentTreeItem);
        }
        if (typeof(bBeforeSelect) != "undefined" && bBeforeSelect != null) {
            if (typeof(bBeforeSelect) == "boolean" && bBeforeSelect == false) 
                return;
        }
    }
    if (eventObject) {
        if (!(_fngetEventTarget(eventObject) && _fngetEventTarget(eventObject).type == "checkbox")) {
            eventObject.preventDefault();
        }
        eventObject.continueBubbling = true;
        eventObject.stopPropagation();
        if (_fngetEventTarget(eventObject) && _fngetEventTarget(eventObject).getAttribute("level") && eventObject.type != "keydown") 
            return;
    }
    if (activeTree != tree) {
        if (activeTree) 
            activeTree.active = false;
        tree.active = true;
        activeTree = tree;
    }
    
    
    if (eventObject && _fngetEventTarget(eventObject) && _fngetEventTarget(eventObject).type == "checkbox") {
        fireOncheckEvent(tree, treeItem, _fngetEventTarget(eventObject));
        if (treeItem.itemType.checkChildren == "true") {
            var container = treeItem.getChildContainer();
            for (var child = container.firstChild; child; child = child.nextSibling) {
                updateAllCheckboxes(tree, child, _fngetEventTarget(eventObject).checked)
            }
        }
    }
    
    if (tree.inpField && tree.inpField.style.display != "none") 
        return;
    tree.gbDoubleClick = false;
    
    if (selectedItem != null) {
        if (treeItem == selectedItem && tree.selectedItems.length == 1) {
            if ((currentTreeItem.itemType.updateAllowed == "true") &&
            (eventObject && eventObject.type != "contextmenu" && eventObject.ctrlKey != true && eventObject.shiftKey != true && _fngetEventTarget(eventObject) == selectedItem.getLabel())) {
                tree.currentItemState = currentTreeItem.isOpen;
                tree.ownerDocument.defaultView.setTimeout(function(){
                    selectedItem.getTreeItem().rename()
                }, 500); // Wait to handle dblclick correctly
                return;
            }
        }
    }
    addToSelected(tree, treeItem, eventObject);
    moveFocus(tree)(null, false, true);
    if (!skipSelect && currentTreeItem.itemType && currentTreeItem.itemType.onselect) {
        with (tree.ownerDocument.defaultView) {
            eval(currentTreeItem.itemType.onselect)(currentTreeItem, eventObject);
        }
    }
}

//handles the execution of the onitemchecked property event
function fireOncheckEvent(tree, treeItem, chkbox){
    if (treeItem.itemType && treeItem.itemType.onitemchecked) {
        var update;
        with (tree.ownerDocument.defaultView) {
            update = eval(treeItem.itemType.onitemchecked)(treeItem);
        }
        if (update == false) {
            chkbox.checked = !chkbox.checked;
        }
    }
}

//handles the execution of the ondblclick property event
function fireOndblclickEvent(tree, treeItem){
    if (treeItem.itemType && treeItem.itemType.ondblclick) {
        var result;
        with (tree.ownerDocument.defaultView) {
            result = eval(treeItem.itemType.ondblclick)(treeItem);
        }
        if (typeof(result) != "undefined" && result == false) {
            treeItem.__cancelExpandCollapse = true;
        }
        else {
            treeItem.__cancelExpandCollapse = false;
        }
    }
}

function addToSelected(tree, item, evt){
    var selectedItems = tree.selectedItems;
    if (tree.TreeSchema.multipleSelect) {
        if (evt && evt.ctrlKey != true) {
            for (var i in selectedItems) {
                selectedItems[i].highlight(false);
                delete selectedItems[i];
            }
            tree.selectedItems = new Array();
        }
        else 
            if (evt && evt.ctrlKey == true) {
                // Check if already selected
                for (var i in selectedItems) {
                    if (selectedItems[i] == item) {
                        var anchor = item.firstChild;
                        if (anchor.outfocused) {
                            showHighlighted(anchor, true);
                            showOutfocused(anchor, false);
                            return;
                        }
                        item.highlight(false);
                        delete selectedItems[i];
                        tree.selectedItem = null;
                        return;
                    }
                }
            }
        tree.selectedItems[tree.selectedItems.length] = item;
    }
    else {
        if (selectedItems.length > 0 && selectedItems[0].getTreeItem().firstChild) 
            selectedItems[0].getTreeItem().highlight(false);
        tree.selectedItems[0] = item;
    }
    item.highlight(true);
    tree.selectedItem = item;
}

function getHighlightFunction(tree){
    if (!tree.__getHighlightFunction) {
        tree.__getHighlightFunction = function(light){
            //If color is changed just to "highlight" and "highlighttext", then the class
            //attribute applied to the element does not work ! To solve the problem, use
            //the style sheet 'highlight' created during initializing
            var anchor = this.firstChild;
            if (!anchor) 
                return;
            if (light) {
                showHighlighted(anchor, true);
                if (anchor.outfocused) {
                    showOutfocused(anchor, false);
                }
            }
            else {
                showHighlighted(anchor, false);
                if (anchor.outfocused) {
                    showOutfocused(anchor, false);
                }
            }
        }
    }
    return tree.__getHighlightFunction;
}

/* Attaches addItem function to either the tree
 dataNode:		the data for the new item
 description:	the name of the item (optional)
 parentItem:		the parent of the new item
 */
Tree.prototype.addItem = function(dataNode, description, parentItem){
    if (!parentItem) {
        if (!this.goTree) 
            this.goTree = createTree(this);
        parentItem = this.goTree;
    }
    return addItemToTree(this, dataNode, description, parentItem);
}

/* attach new addItem function to a treeItem
dataNode:		the data for the new item
description:	the name of the item (optional)
parentItem:		the parent of the new item
*/
function getAddItemFunction(tree){
    if (!tree.__getAddItemFunction) {
        tree.__getAddItemFunction = function(dataNode, description, parentItem){
            if (!parentItem) 
                parentItem = this;
            return addItemToTree(tree, dataNode, description, parentItem);
        }
    }
    return tree.__getAddItemFunction;
}

/* This functions adds a single tree item , and just the top node. So if the dataNode has may children these are not rendered
 until the tree item is expanded. An item is created using an innerHTML update. So when there are already children the new item is created
 in a tempNode and later appended to the parentItem.
 tree:			the active tree
 dataNode:		xml node containing the data for the node to be added (the HTML of the top element is rendered)
 description:	description of the node, if null the description is retrieved from the dataNode
 parentItem:		the parent of the newly added item
 */
function addItemToTree(tree, dataNode, description, parentItem){
    var container = getNodeContainer(parentItem);
    if (!container.firstChild && !parentItem.childrenRendered && parentItem.data && !parentItem.isOpen && tree.treeData && !parentItem.isLeaf) {
        //the existing children of parentItem have not been rendered
        //do so before other treeItems are added
        createTreeLevel(tree, parentItem.data, parentItem);
        if (dataNode) {
            var curParent = dataNode.parentNode;
            while (curParent && curParent != parentItem.data) 
                curParent = curParent.parentNode;
            if (curParent) {
                // node already created by rendering the parent. Find the right item to be returned
                var child = container.firstChild;
                while (child) {
                    if (child.data == dataNode) 
                        return child;
                    child = child.nextSibling;
                }
                return null; // not found, but added somewhere already
            }
        }
    }
    
    var itemType = checkIfNode(tree, dataNode);
    if (!itemType) 
        return null;
    var isLeaf = isTreeItemALeaf(tree, dataNode, itemType);
    var treeNode;
    
    //create the html string for a single tree item	
    if (parentItem.insertionPoint) {
        insertHTML(parentItem.insertionPoint, "beforeBegin", createSingleNodeString(tree, dataNode, itemType, isLeaf, parseInt(parentItem.getAttribute("level")) + 1));
        treeNode = parentItem.insertionPoint.previousSibling;
        parentItem.insertionPoint = null;
    }
    else {
        insertHTML(container, "beforeEnd", createSingleNodeString(tree, dataNode, itemType, isLeaf, parseInt(parentItem.getAttribute("level")) + 1));
        treeNode = container.lastChild;
    }
    
    if (!tree.root) 
        tree.setRoot(treeNode);
    //attach all the methods event properties to the created tree item
    attachEventsMethodsPropertiesSingleNode(tree, treeNode, dataNode, isLeaf, itemType, parentItem.isTree);
    
    setParentProperties(tree, parentItem);
    if (!treeNode.isRoot && treeNode.previousSibling) 
        setPlusImage(tree, treeNode.previousSibling);
    
    if (description != null && typeof(description) != "undefined") 
        treeNode.setLabel(description);
    return treeNode;
}

/*attach addItems function to tree
 dataNodes:		array of data nodes which create the new items
 parentItem:		the parent of the new items
 */
Tree.prototype.addItems = function(dataNodes, parentItem){
    if (!parentItem) {
        if (!this.goTree) 
            this.goTree = createTree(this);
        parentItem = this.goTree;
    }
    return addItemsToTree(this, dataNodes, parentItem);
}

/*addItems function for treeItems
 dataNodes:		array of data nodes which create the new items
 parentItem:		the parent of the new items
 */
function getAddItemsFunction(tree){
    if (!tree.__getAddItemsFunction) {
        tree.__getAddItemsFunction = function(dataNodes, parentItem){
            if (!parentItem) 
                parentItem = this;
            return addItemsToTree(tree, dataNodes, parentItem);
        }
    }
    return tree.__getAddItemsFunction;
}

/* 
 Adds an array of dataNodes to one parent. First the string for all the dataNodes is created and the innerHTML is written.
 After which the methods/events/properties are attached.
 tree:		active tree
 dataNodes:	array with dataNodes to be added to the parentItem
 parentItem:	the treeItem getting the new items
 */
function addItemsToTree(tree, dataNodes, parentItem){
    if (dataNodes.length > 0) {
        var container = getNodeContainer(parentItem);
        
        if (!container.firstChild && !parentItem.childrenRendered && parentItem.data && !parentItem.isOpen && tree.treeData && !parentItem.isLeaf) {
            //the existing children of parentItem have not been rendered\
            //do so before other treeItems are added
            var dataNodesForRender = getXMLNodes(tree, parentItem.data, tree.TreeSchema.contSearch);
            createTreeLevelFromDataArray(tree, dataNodesForRender, parentItem, false);
        }
        var oldLastNode = container.lastChild;
        //now datanodes can be added to the parentItem.
        var result = createTreeLevelFromDataArray(tree, dataNodes, parentItem, true);
        if (oldLastNode) {
            //the parent already had children, update the plus/minus image of its (formally) last child.
            setPlusImage(tree, oldLastNode);
        }
        return result;
    }
}

function getAddItemBeforeFunction(tree){
    if (!tree.__getAddItemBeforeFunction) {
        tree.__getAddItemBeforeFunction = function(xml, nextItem){
            this.insertionPoint = nextItem;
            return addItemToTree(tree, xml, null, this);
        }
    }
    return tree.__getAddItemBeforeFunction;
}

function getMoveItemFunction(tree){
    if (!tree.__getMoveItemFunction) {
        tree.__getMoveItemFunction = function(newParent){
            if (this.getParentItem() == newParent) 
                return;
            var oldParentContainer = getNodeContainer(this.getParentItem());
            // Move the xml element to the new parent
            if (newParent.data) 
                newParent.data.appendChild(this.data);
            // Move the item in a new parent in the tree
            var parentContainer = getNodeContainer(newParent);
            this.setAttribute("level", parseInt(newParent.getAttribute("level")) + 1);
            if (newParent.isTree) {
                this.isRoot = true;
                if (!root) 
                    root = this;
            }
            if (newParent.insertionPoint) {
                parentContainer.insertBefore(this, newParent.insertionPoint);
                newParent.insertionPoint = null;
            }
            else {
                parentContainer.appendChild(this);
            }
            if (newParent.isLeaf) // parent is no leaf anymore
            {
                leafToNode(tree, newParent);
            }
            setContainerConnectLine(tree, this);
            if (parentContainer.childNodes.length > 1) // previous child is no longer the last one
            {
                setPlusImage(tree, parentContainer.lastChild.previousSibling);
            }
            if (oldParentContainer.childNodes.length > 0) // last child of old parent gets new image
            {
                setPlusImage(tree, oldParentContainer.lastChild);
                setContainerConnectLine(tree, oldParentContainer.lastChild);
            }
            setPlusImage(tree, this);
        }
    }
    return tree.__getMoveItemFunction;
}

function getCutItemFunction(tree){
    if (!tree.__getCutItemFunction) {
        tree.__getCutItemFunction = function(deep, skipCut){
            if (!deep) 
                deep = false;
            if (!skipCut) 
                skipCut = false;
            var currentTreeItem = tree.currentTreeItem;
            if (!skipCut && currentTreeItem.itemType && currentTreeItem.itemType.oncut) {
                if (eval("tree.ownerDocument.defaultView." + currentTreeItem.itemType.oncut + "(currentTreeItem)") == false) 
                    return;
            }
            tree.copiedItem = this.data.cloneNode(true);
            if (!copypasteTreeData) 
                copypasteTreeData = {};
            copypasteTreeData["XML"] = tree.copiedItem;
            if (!deep) {
                for (var i = 0; i < tree.copiedItem.childNodes.length; i++) {
                    if (Docex.selectXMLNodes(tree.copiedItem.childNodes[i], "*").length > 0) {
                        tree.copiedItem.removeChild(tree.copiedItem.childNodes[i--]);
                    }
                }
                var parent = this.getParentItem();
                if (parent && getNodeContainer(this)) {
                    // Move all childs of item to the parentNode
                    var child = getNodeContainer(this).firstChild;
                    while (child) {
                        var nextChild = child.nextSibling;
                        child.moveTo(parent);
                        child = nextChild;
                    }
                }
            }
            else {
                if (this.data.parentNode) {
                    this.data.parentNode.removeChild(this.data);
                }
                else {
                    this.data = null;
                }
            }
            this.remove(true);
        }
    }
    return tree.__getCutItemFunction;
}

function getCopyItemFunction(tree){
    if (!tree.__getCopyItemFunction) {
        tree.__getCopyItemFunction = function(deep, skipCopy){
            if (!deep) 
                deep = false;
            if (!skipCopy) 
                skipCopy = false;
            var currentTreeItem = tree.currentTreeItem;
            if (!skipCopy && currentTreeItem.itemType && currentTreeItem.itemType.oncopy) {
                if (eval("tree.ownerDocument.defaultView." + currentTreeItem.itemType.oncopy + "(currentTreeItem)") == false) 
                    return;
            }
            tree.copiedItem = this.data.cloneNode(true);
            if (!deep) {
                for (var i = 0; i < tree.copiedItem.childNodes.length; i++) {
                    if (Docex.selectXMLNodes(tree.copiedItem.childNodes[i], "*").length > 0) {
                        tree.copiedItem.removeChild(tree.copiedItem.childNodes[i--]);
                    }
                }
            }
            if (!copypasteTreeData) 
                copypasteTreeData = {};
            copypasteTreeData["XML"] = tree.copiedItem;
        }
    }
    return tree.__getCopyItemFunction;
}

function getPasteItemFunction(tree){
    if (!tree.__getPasteItemFunction) {
        tree.__getPasteItemFunction = function(skipPaste){
            if (!skipPaste) 
                skipPaste = false;
            try {
                var currentTreeItem = tree.currentTreeItem;
                if (!skipPaste && currentTreeItem.itemType && currentTreeItem.itemType.onpaste) {
                    if (eval("tree.ownerDocument.defaultView." + currentTreeItem.itemType.onpaste + "(currentTreeItem)") == false) 
                        return;
                }
                if (tree.copiedItem) {
                    addItemToTree(tree, tree.copiedItem, null, this);
                    this.data.appendChild(tree.copiedItem);
                }
                else {
                    // Check if there is something in the windows clipboard
                    if (copypasteTreeData && copypasteTreeData["XML"]) {
                        var clipboardData = copypasteTreeData["XML"];
                        addItemToTree(tree, clipboardData, null, this);
                        this.data.appendChild(clipboardData);
                    }
                }
            } 
            catch (e) {
            }
        }
    }
    return tree.__getPasteItemFunction;
}

function getRemoveFunction(tree){
    if (!tree.__getRemoveFunction) {
        tree.__getRemoveFunction = function(toCut, eventObject){
            // This event is fired after an item is removed from the tree.
            if (this.isRoot) 
                tree.setRoot(null);
            var customEvent = new Object();
            customEvent.treeItem = this;
            customEvent.srcEvent = eventObject || tree.ownerDocument.defaultView.event;
            raiseEvent(tree, "onremoveitem", customEvent);
            if (customEvent.returnValue == false) 
                return;
            
            var pItem = this.isAtRootLevel ? this.parentNode : this.getParentItem();
            if (this == tree.selectedItem) {
                tree.selectedItem = null;
                if (!tree.TreeSchema.multipleSelect) 
                    tree.selectedItems.length = 0;
            }
            //Remove the item(s) from the selectedItems array
            for (var i in tree.selectedItems) {
                if (tree.selectedItems[i] == this) {
                    delete tree.selectedItems[i];
                }
            }
            var treeNode = this.getTreeItem();
            //can not just do this also remove the data node otherwise rerendered when parent is reexpanded
            /*var data = treeNode.data;
             if (data && data.parentNode){
             data.parentNode.removeChild(data);
             }*/
            removeEvents(tree, treeNode);
            removeMethods(tree, treeNode, toCut);
            //tree.ownerDocument.defaultView.application.addGarbage(treeNode);
            treeNode = null;
            if (!pItem) 
                return;
            if (this.isAtRootLevel) {
                redrawTreeOnItemRemove(tree, pItem);
            }
            else {
                var parentUL = getNodeContainer(pItem);
                if (!redrawTreeOnItemRemove(tree, getNodeContainer(pItem))) {
                    if (pItem != tree.goTree) 
                        pItem.collapse();
                }
            }
        }
    }
    return tree.__getRemoveFunction;
}

function redrawTreeOnItemRemove(tree, itemParent){
    if (itemParent.childNodes && itemParent.childNodes.length > 0) {
        drawTreeNode(tree, itemParent.lastChild);
        return true;
    }
    else 
        return false;
}

function getRenameFunction(tree){
    if (!tree.__getRenameFunction) {
        tree.__getRenameFunction = function(){
            if (tree.gbDoubleClick) 
                return tree.gbDoubleClick = false;
            var customEvent = new Object();
            customEvent.returnValue = true;
            customEvent.selectedNode = this.getTreeItem();
            raiseEvent(tree, "onbeforerename", customEvent);
            if (customEvent.returnValue) {
                if (tree.inpField && tree.inpField.style.display != "none") 
                    return;
                tree.currentTreeItem = this;
                createInputField(tree, true);
                tree.renameMode = true;
            }
            else {
                if (tree.inpField && tree.inpField.style.display != "none") {
                    tree.inpField.style.display = "none";
                    tree.currentTreeItem.getLabel().style.display = "inline";
                }
                tree.renameMode = false;
            }
        }
    }
    return tree.__getRenameFunction;
}

function getChangeIconFunction(tree){
    if (!tree.__getChangeIconFunction) {
        tree.__getChangeIconFunction = function(url){
            if (typeof(url) == "string") {
                if (this.data) {
                    this.iconChanged = true;
                    this.iconUrl = url;
                    setIcon(this);
                }
                else {
                    setIconSrc(getFirstImageNode(this), url);
                }
            }
            else 
                if (typeof(url) == "object") {
                    if (url.length < 1) 
                        return;
                    var images = getItemImages(this);
                    if (this.data) {
                        this.iconChanged = true;
                        this.iconUrl = url[0];
                    }
                    if (url.length > images.length) {
                        //more images are changed than present -> the remainer must be added.
                        for (var i = images.length; i < url.length; i++) {
                            var labelNode = this.getLabel();
                            var imageContainer = "<div class='" + CL_IMAGECONTAINER + "' draggable='false' style=\"background-image:url('" + url[i] + "')\">" +
                            "</div>";
                            insertHTML(labelNode, "beforeBegin", imageContainer);
                            labelNode.previousSibling.getTreeItem = getTreeItem;
                        }
                    }
                    
                    for (var i = 0; i < images.length; i++) {
                        setIconSrc(images[i], url[i]);
                    }
                    
                    //Since images can be added, be sure the events are set once again
                    setEvents(tree, this);
                }
        }
    }
    return tree.__getChangeIconFunction;
}

function getItemImages(treeItem){
    var imageNodes = new Array();
    var firstChild = treeItem.firstChild.firstChild;
    for (var child = firstChild; child;) {
        if (HTMLUtil.hasClassName(CL_IMAGECONTAINER, child)) {
            imageNodes[imageNodes.length] = child;
        }
        child = child.nextSibling;
    }
    return imageNodes;
}

function getChangeOpenIconFunction(tree){
    if (!tree.__getChangeOpenIconFunction) {
        tree.__getChangeOpenIconFunction = function(url){
            if (url) {
                this.iconChanged = true;
                this.iconOpenUrl = url;
                setIcon(this);
            }
        }
    }
    return tree.__getChangeOpenIconFunction;
}

function createInputField(tree, startDirect){
    var currentTreeItem = tree.currentTreeItem;
    if (startDirect || tree.currentItemState == currentTreeItem.isOpen) {
        if (!tree.inpField) {
            // Create the input field once
            tree.inpField = tree.ownerDocument.createElement("input");
            tree.inpField.className = CL_ITEMINPUT + " input";
            tree.inpField.id = "tree-input";
            _fnaddListener(tree.inpField, "onchange", htmlChangeListener(tree));
            _fnaddListener(tree.inpField, "onblur", leaveInputField(tree));
        }
        // Show input field on top of the selected item	
        var labelNode = currentTreeItem.getLabel();
        tree.inpField.style.width = (labelNode.offsetWidth + 100) + "px";
        currentTreeItem.firstChild.draggable = false;
        HTMLUtil.removeClassName("draggable", currentTreeItem.firstChild);
        //add the input field just before the label (span)
        currentTreeItem.firstChild.insertBefore(tree.inpField, labelNode);
        tree.inpField.value = _fngetTextContent(labelNode);
        labelNode.style.display = "none";
        tree.inpField.style.display = "inline";
        tree.inpField.getTreeItem = getTreeItem;
        tree.ownerDocument.defaultView.setTimeout(setFocus(tree, tree.inpField), 20);
    }
}

function setFocus(tree, inputField){
    return function(){
        inputField.focus();
        inputField.select();
        //left folding -> make sure the input is into view
        scrollLeftFoldingIntoView(tree, inputField.parentNode.parentNode);
    }
}

function scrollLeftFoldingIntoView(tree, treeItem){
    if (tree.leftFolding && treeItem.tagName.toLowerCase() == "li") {
        //scroll horizontally into view
        var right = Math.abs(parseInt(_fngetComputedStyle(treeItem.parentNode).right));
        var level = parseInt(treeItem.level);
        var maxScroll = tree.firstChild.offsetWidth - tree.clientWidth;
        if (maxScroll > 0) {
            //root is level 2, if root clicked -> scroll max right, parent also visible (+right)
            tree.scrollLeft = maxScroll - (level - 2) * right - 2 * right;
        }
    }
}

function htmlChangeListener(tree){
    if (!tree.__htmlChangeListener) {
        tree.__htmlChangeListener = function(eventObject){
            if (!eventObject) 
                eventObject = tree.ownerDocument.defaultView.event;
            var tItem = tree.currentTreeItem.getTreeItem();
            if (!tItem.itemType.onchange && !tree.BusDataIsland) {
                // if there is no onchange method defined, default action is change the corresponding xml too.
                // This cannot be done when busdataisland is used, that should work with old/new pairs.
                var node = getXMLNode(tree, tItem.data, tItem.itemType.text); //NOMBV
                if (node.nodeType == 1) 
                    _fnsetTextContent(node, _fngetEventTarget(eventObject).value);
                else 
                    node.nodeValue = _fngetEventTarget(eventObject).value;
            }
        }
    }
    return tree.__htmlChangeListener;
}

function leaveInputField(tree){
    if (!tree.__leaveInputField) {
        tree.__leaveInputField = function(eventObject){
            if (!eventObject) {
                eventObject = tree.ownerDocument.defaultView.event;
            }
            // Hide the input field and show text again with new value.
            _fngetEventTarget(eventObject).style.display = "none";
            var currentTreeItem = tree.currentTreeItem;
            currentTreeItem.getLabel().style.display = "inline";
            currentTreeItem.firstChild.draggable = true;
            HTMLUtil.addClassName("draggable", currentTreeItem.firstChild);
            tree.renameMode = false;
            var selItem = currentTreeItem.getTreeItem();
            if (eventObject && (typeof(_fngetEventTarget(eventObject).cancelRename) == "undefined" || _fngetEventTarget(eventObject).cancelRename == false)) {
                with (tree.ownerDocument.defaultView) {
                    if (selItem && selItem.itemType.onchange && (eval(selItem.itemType.onchange)(selItem, _fngetTextContent(selItem.getLabel()), _fngetEventTarget(eventObject).value, eventObject)) == false) 
                        return;
                }
                _fnsetTextContent(currentTreeItem.getLabel(), _fngetEventTarget(eventObject).value);
                fireOnAfterRename(tree, selItem, false);
            }
            else 
                if (_fngetEventTarget(eventObject).cancelRename) {
                    fireOnAfterRename(tree, selItem, true);
                    _fngetEventTarget(eventObject).cancelRename = false;
                }
        }
    }
    return tree.__leaveInputField;
}

function fireOnAfterRename(tree, selItem, cancelRename){
    var customEvent = new Object();
    customEvent.selectedNode = selItem;
    customEvent.cancelRename = cancelRename;
    raiseEvent(tree, "onafterrename", customEvent);
}

function getContinueDraggingFunction(tree){
    if (!tree.__getContinueDraggingFunction) {
        tree.__getContinueDraggingFunction = function(eventObject){
            if (!eventObject) 
                eventObject = tree.ownerDocument.defaultView.event;
            eventObject.preventDefault();
            eventObject.continueBubbling = true;
            eventObject.stopPropagation();
            tree.gDropCurrent = getListNode(_fngetEventTarget(eventObject)).getTreeItem();
            var isDraggedFromSelf = true;
            // Check for recursion
            var tmp = tree.gDropCurrent;
            var userDropEffect = null;
            do {
                if (tmp == tree.gDraggingItem) {
                    if (tree.gDropPrevious) 
                        tree.gDropPrevious.highlight(false);
                    eventObject.dataTransfer.dropEffect = "none";
                    setHighlight(tree, false);
                    return;
                }
                tmp = tmp.getParentItem();
            }
            while (tmp);
            setHighlight(tree, true);
            if (gDraggedTreeItem && gDraggedTreeItem.treeID && gDraggedTreeItem.treeID != "dx" + (objectCounter++)) {
                // entering another tree, simulating the dragged item in this tree
                try {
                    tree.gDraggingItem = gDraggedTreeItem.treeData;
                    tree.gDropEffect = "none";
                    isDraggedFromSelf = false;
                    // make sure things get cleaned up if we do nothing in this tree:
                    tree.gDraggingTimeout = tree.ownerDocument.defaultView.setTimeout(getDragEndFunction(tree), 500);
                } 
                catch (e) {
                    // nice try, but something went wrong, the dragged stuff might not be coming from another tree...
                    tree.gDraggingItem = null;
                    eventObject.dataTransfer.dropEffect = tree.gDropEffect = "none";
                    return;
                }
            }
            if (tree.gDropCurrent.itemType.dragEnabled == "true" && tree.gDropCurrent.itemType.ondragenter) {
                with (tree.ownerDocument.defaultView) {
                    tree.gDropEffect = eval(tree.gDropCurrent.itemType.ondragenter)(tree.gDraggingItem, eventObject);
                }
                if (!tree.gDropEffect) 
                    tree.gDropEffect = "";
                if (tree.gDropEffect != "copy" && tree.gDropEffect != "move" && tree.gDropEffect != "none") 
                    tree.gDropEffect = "none";
                else 
                    userDropEffect = tree.gDropEffect;
            }
            
            if ((tree.gDropEffect != "none" || tree.__isMovedOverSameNode) && tree.gDraggingItem && tree.gDraggingItem.itemType.dropTarget) //NEW DRAG
            {
                if (!tree.gDropCurrent.isLeaf && !tree.gDropCurrent.isOpen) {
                    tree.ownerDocument.defaultView.setTimeout(doExpand(tree, tree.gDropCurrent), 1000);
                }
                if (tree.gExprDropTarget && tree.gExprDropTarget.test(tree.gDropCurrent.data.tagName)) {
                    if (tree.gDropEffect != "none" || tree.__isMovedOverSameNode) {
                        if (eventObject.ctrlKey == true) 
                            tree.gDropEffect = "copy";
                        else 
                            tree.gDropEffect = "move";
                        
                        tree.__isMovedOverSameNode = false;
                    }
                }
                else {
                    tree.gDropEffect = "none";
                    tree.__isMovedOverSameNode = true;
                }
            }
            else {
                tree.gDropEffect = "none";
                if (gDraggedTreeItem) //dragged item is a tree item
                {
                    if (gDraggedTreeItem.treeID)//gDraggedTreeItem is dragEnabled
                    {
                        if (isDraggedFromSelf) {
                            expandNodeForOutsideElement(tree, isDraggedFromSelf);
                        }
                        else {
                            isDraggedFromSelf = false;
                            expandNodeForOutsideElement(tree, isDraggedFromSelf);
                        }
                    }
                }
                else {
                    isDraggedFromSelf = false;
                    expandNodeForOutsideElement(tree, isDraggedFromSelf);
                }
                if (tree.gDropEffect != null && tree.gDropEffect != "none") {
                    if (userDropEffect) {
                        tree.gDropEffect = userDropEffect;
                    }
                    else 
                        if (typeof(eventObject.ctrlKey) != "undefined") // eventObject.ctrlKey undefined for CHROME and SAFARI
                        {
                            if (eventObject.ctrlKey == true) 
                                tree.gDropEffect = "copy";
                            else 
                                tree.gDropEffect = "move";
                        }
                }
            }
            
            if (tree.gDropEffect) {
                if (tree.gDropEffect == "none") {
                    eventObject.dataTransfer.dropEffect = "none";
                }
                else {
                    // eventObject.dataTransfer.dropEffect should always be copy to work in Chrome
                    eventObject.dataTransfer.dropEffect = "copy";
                    setHighlight(tree, true);
                }
            }
        }
    }
    return tree.__getContinueDraggingFunction;
}

function expandNodeForOutsideElement(tree, isDraggedFromSelf){
    if (isDraggedFromSelf || tree.genericDropEnabled) {
        if (!tree.gDropCurrent.isOpen) 
            tree.ownerDocument.defaultView.setTimeout(doExpand(tree, tree.gDropCurrent), 500);
        if (tree.gDropCurrent.itemType.genericDropEnabled || isDraggedFromSelf) 
            tree.gDropEffect = "";
    }
    
}

function doExpand(tree, elmt){
    return function(){
        if (tree.gDropCurrent == elmt) 
            tree.gDropCurrent.expand();
    }
}

function getDragStartFunction(tree){
    if (!tree.__getDragStartFunction) {
        tree.__getDragStartFunction = function(eventObject){
            tree.gDropPrevious = null;
            if (!eventObject) 
                eventObject = tree.ownerDocument.defaultView.event;
            var node = _fngetEventTarget(eventObject).getTreeItem();
            if (node) {
                gDraggedTreeItem = new Object();
                gDraggedTreeItem.treeID = "dx" + (objectCounter++);
                gDraggedTreeItem.treeData = node;
                
                tree.gDraggingItem = node;
                if (tree.gDraggingItem.itemType && tree.gDraggingItem.itemType.dropTarget) {
                    tree.gExprDropTarget = new RegExp(tree.gDraggingItem.itemType.dropTarget);
                }
                if (node.itemType) {
                    with (tree.ownerDocument.defaultView) {
                        if (node.itemType.ondrag && (eval(node.itemType.ondrag)(node, eventObject)) == false) {
                            eventObject.stopPropagation();
                            return;
                        }
                    }
                }
                if (typeof(node.text) != "undefined" && node.text != null) //NOMBV
                {
                    dataTransferSetData(eventObject, 'Text', node.text); //NOMBV
                }
                else {
                    dataTransferSetData(eventObject, 'Text', Docex.getXML(node.data));
                }
                eventObject.dataTransfer.effectAllowed = "copyMove";
                tree.ctrlDragged = eventObject.ctrlKey;
            }
        }
    }
    return tree.__getDragStartFunction;
}

function getDragAnythingFunction(tree){
    if (!tree.__getDragAnythingFunction) {
        tree.__getDragAnythingFunction = function(eventObject){
            tree.gDropPrevious = null;
            gDraggedTreeItem = new Object();
            if (!eventObject) 
                eventObject = tree.ownerDocument.defaultView.event;
            
            // used for dragging items outside the tree. Cannot be dropped on any tree item. Drop should be handled in destination application.
            var node = _fngetEventTarget(eventObject).getTreeItem();
            if (!node.isRoot) {
                eventObject.gDraggingItem = tree.gDraggingItem; //DRAG add
                tree.gDraggingItem = null;
                if (node.itemType) {
                    with (tree.ownerDocument.defaultView) {
                        if (node.itemType.ondrag && (eval(node.itemType.ondrag)(node, eventObject)) == false) {
                            eventObject.stopPropagation();
                            return;
                        }
                    }
                }
                if (typeof(node.text) != "undefined" && node.text != null) //NOMBV
                {
                    dataTransferSetData(eventObject, 'Text', node.text); //NOMBV
                }
                else {
                    var dataNode = node.data.cloneNode(true);
                    setOrganization(dataNode, node);
                    dataTransferSetData(eventObject, 'Text', (new XMLSerializer()).serializeToString(dataNode));
                }
                eventObject.dataTransfer.effectAllowed = "copyMove";
                tree.ctrlDragged = eventObject.ctrlKey;
            }
        }
    }
    return tree.__getDragAnythingFunction;
}

// Function returns the organizational context of the selected node.
// This is set in the root of the browser.
function getOrganization(selectedNode){
    if (selectedNode.organization) 
        return selectedNode.organization;
    if (selectedNode.getParentItem()) 
        return getOrganization(selectedNode.getParentItem());
    return "";
}

function setOrganization(dataNode, node){
    if (dataNode.tagName == "Application") {
        var orgDN = getOrganization(node);
        
        if (orgDN == "") 
            return; //if no organization context
        var orgNode = _fnselectXMLNode(dataNode, "*[local-name()='organization']");
        if (!orgNode) {
            orgNode = dataNode.ownerDocument.createElement("organization");
            dataNode.appendChild(orgNode);
        }
        Docex.setTextContent(orgNode, orgDN)
    }
}

function getDragLeaveFunction(tree){
    if (!tree.__getDragLeaveFunction) {
        tree.__getDragLeaveFunction = function(){
            tree.gDropCurrent = null;
        }
    }
    return tree.__getDragLeaveFunction;
}

function getDragEndFunction(tree){
    if (!tree.__getDragEndFunction) {
        tree.__getDragEndFunction = function(eventObject){
            if (!eventObject) 
                eventObject = tree.ownerDocument.defaultView.event;
            if (eventObject) {
                try {
                    eventObject.continueBubbling = true;
                    eventObject.stopPropagation();
                    eventObject.preventDefault();//DRAG changed
                    var effect = eventObject.dataTransfer.dropEffect;
                    if (!(effect == "copy" || effect == "move")) //fix for chrome as ondragend gets called before ondrop
                        resetDragObjects(eventObject, tree);
                } 
                catch (e) {
                    //don't do anything as the event object was not proper
                }
            }
        }
    }
    return tree.__getDragEndFunction;
}

function resetDragObjects(eventObject, tree){
    var node = _fngetEventTarget(eventObject).getTreeItem();
    if (node) {
        if (node.itemType.dragEnabled == "true") {
            _fnremoveListener(node, "ondragstart", getDragStartFunction(tree));
        }
        else 
            if (node.itemType.dragEnabled == "false") {
                _fnremoveListener(node, "ondragstart", getDragAnythingFunction(tree));
            }
    }
    gDraggedTreeItem = null;
    tree.gDropEffect = null;
    tree.gDraggingItem = null;
    tree.gDropCurrent = null;
    if (tree.gDropPrevious && tree.gDropPrevious.highlight) 
        tree.gDropPrevious.highlight(false);
    tree.gDropPrevious = null;
    tree.gExprDropTarget = null;
}

function getDragStopFunction(tree){
    if (!tree.__getDragStopFunction) {
        tree.__getDragStopFunction = function(eventObject){
            if (!eventObject) 
                eventObject = tree.ownerDocument.defaultView.event;
            eventObject.preventDefault();
            eventObject.continueBubbling = true;
            eventObject.stopPropagation();
            var node = _fngetEventTarget(eventObject).getTreeItem();
            var droppedItem = tree.gDraggingItem;
            if (!tree.gDraggingItem) {
                if (node.itemType.genericDropEnabled) {
                    if (!xmlDocument) 
                        xmlDocument = Docex.getXMLDocument();
                    var draggedData = xmlDocument.loadXML("<droppeditem>" + dataTransferGetData(eventObject, 'Text') + "</droppeditem>");
                    var droppedItem = new Object();
                    droppedItem.data = draggedData;
                    dragRenderChildren(tree, node);
                    setHighlight(tree, false);
                    if (node.itemType.ondrop) {
                        with (tree.ownerDocument.defaultView) {
                            eval(node.itemType.ondrop)(droppedItem, node, tree.gDropEffect, eventObject);
                        }
                    }
                }
                return;
            }
            //before the item is dropped on the target. The children of the target must be rendered
            dragRenderChildren(tree, node);
            setHighlight(tree, false);
            tree.gDraggingItem = null;
            with (tree.ownerDocument.defaultView) {
                if (node.itemType.ondrop && (eval(node.itemType.ondrop)(droppedItem, node, tree.gDropEffect, eventObject)) == false) 
                    return;
                if (droppedItem.itemType.ondragend && (eval(droppedItem.itemType.ondragend)(droppedItem, node, tree.gDropEffect, eventObject)) == false) 
                    return;
            }
            
            var newItem = null;
            
            if (tree.ctrlDragged) {
                newItem = droppedItem.cloneNode(true);
                newItem.data = droppedItem.data.cloneNode(true);
                addItemToTree(tree, newItem.data, null, node);
                node.data.appendChild(newItem.data); //Append dropped item to the new node
            }
            else {
                newItem = droppedItem;
                if (node.data != null) {
                    addItemToTree(tree, newItem.data, null, node);
                    node.data.appendChild(droppedItem.data); //Append dropped item to the new node
                }
                
                droppedItem.remove();
            }
            resetDragObjects(eventObject, tree);
        }
    }
    return tree.__getDragStopFunction;
}

/*Renders the children of treeItem
 */
function dragRenderChildren(tree, treeItem){
    var container = getNodeContainer(treeItem);
    if (!container.firstChild && !treeItem.childrenRendered && treeItem.data && !treeItem.isOpen && tree.treeData && !treeItem.isLeaf) {
        //the existing children of treeItem have not been rendered\
        //do so before other treeItems are added
        var dataNodesForRender = getXMLNodes(tree, treeItem.data, tree.TreeSchema.contSearch);
        createTreeLevelFromDataArray(tree, dataNodesForRender, treeItem, false);
        treeItem.childrenRendered = true;
    }
}

//This function sets or removes highlight from a tree item - while dragging / dropping items
function setHighlight(tree, highlight){
    if (highlight) {
        //Highlights the selected item when dragged
        if (tree.gDropPrevious && tree.gDropPrevious != tree.gDropCurrent) {
            //Check if the item is already selected and if it is not, then remove the highlight
            if (tree.gDropPrevious.highlight && !isSelected(tree, tree.gDropPrevious)) 
                tree.gDropPrevious.highlight(false);
            tree.gDropPrevious = tree.gDropCurrent;
        }
        tree.gDropCurrent.highlight(true);
        tree.gDropPrevious = tree.gDropCurrent;
        moveFocus(tree)(null, true);
    }
    else {
        //Removes highlight from a selected item when dropped
        if (tree.gDropCurrent && !isSelected(tree, tree.gDropCurrent)) {
            tree.gDropCurrent.highlight(false);
            moveFocus(tree)(null, false);
        }
    }
}

//Finds if the item is selected
function isSelected(tree, currentItem){
    for (var i in tree.selectedItems) {
        if (tree.selectedItems[i] == currentItem) 
            return true;
    }
    return (tree.selectedItem == currentItem);
}

//------------------------------------------------------------------------
//  Function:  fnOnKeyPress
//  Synopsis:  When the user presses a key on the tree, do the right action
//			   The following keys are supported:
//					- [arrow right]			: expand current node
//					- [arrow left]			: collapse current node
//					- [arrow up]			: goto prev item
//					- [arrow down]			: goto next item
//					- <Esc>					: close the input, if opened
//					- <Enter>				: expand/collapse current node
//					- F2					: edit current item
//------------------------------------------------------------------------
function fnOnKeyDown(tree){
    if (!tree.__fnOnKeyDown) {
        tree.__fnOnKeyDown = function(eventObject){
            fnOnKeyDownFunction(tree, eventObject);
        }
    }
    return tree.__fnOnKeyDown;
}

function fnOnKeyDownFunction(tree, eventObject){
    if (tree.active == false && !tree.renameMode) {
        return;
    }
    if (!eventObject) 
        eventObject = tree.ownerDocument.defaultView.event;
    if (!eventObject) 
        return;
    var currentTreeItem = tree.currentTreeItem;
    //In case we are inside some other element apart from tree item, return.
    if (currentTreeItem != _fngetEventTarget(eventObject) && ((eventObject.keyCode >= 37 && eventObject.keyCode <= 40) || (_fngetEventTarget(eventObject) != tree.inpField && eventObject.keyCode == 13))) {
        var itemName = _fngetEventTarget(eventObject).tagName.toLowerCase();
        //Check for input, textarea, select and 'contentEditable' DIV where naviation is possible inside.
        if (itemName == "input" || itemName == "textarea" || itemName == "select" || (itemName == "div" && _fngetEventTarget(eventObject).contentEditable == "true")) 
            return;
    }
    var isEventHandled = false;
    eventObject.isTreeRenamed = false;
    var selectedItem = tree.selectedItem;
    switch (eventObject.keyCode) {
        case 9: //Tab is pressed. change the highighted color to indicate that it does not have the focus currently
            moveFocus(tree)(null, true);
            eventObject.stopPropagation();
            break;
        case 39: // arrow right
            if (currentTreeItem && currentTreeItem.isLeaf == false) {
                if (!currentTreeItem.isOpen) {
                    currentTreeItem.expand();
                    isEventHandled = true;
                }
            }
            break;
        case 37: // arrow left
            if (currentTreeItem) {
                if (currentTreeItem.isLeaf == false && currentTreeItem.isOpen) {
                    currentTreeItem.collapse();
                    isEventHandled = true;
                }
            }
            break;
        case 38: //arrow up
            if (currentTreeItem && currentTreeItem.parentNode) {
                var newItem = currentTreeItem.previousSibling;
                if (newItem) {
                    while (newItem.isOpen) {
                        if (getNodeContainer(newItem).lastChild) 
                            newItem = getNodeContainer(newItem).lastChild;
                        else 
                            break;
                    }
                    delayedSelect(tree, newItem);
                }
                else {
                    delayedSelect(tree, currentTreeItem.getParentItem());
                }
                isEventHandled = true;
            }
            break;
        case 40: //arrow down
            if (currentTreeItem) {
                if (currentTreeItem.isLeaf == false && currentTreeItem.isOpen && !tree.gRecursive && getNodeContainer(currentTreeItem).firstChild) {
                    // Get first child item in current item
                    delayedSelect(tree, getNodeContainer(currentTreeItem).firstChild);
                }
                else {
                    if (currentTreeItem.nextSibling) {
                    
                        // Get next item in same parent
                        delayedSelect(tree, currentTreeItem.nextSibling);
                    }
                    else {
                        // Get next item in grandparent
                        if (currentTreeItem.parentNode) {
                            var saveCurrent = tree.currentTreeItem;
                            tree.currentTreeItem = tree.currentTreeItem.parentNode;
                            tree.gRecursive = true;
                            //fnOnKeyDown(tree)(); //FF does not like this construction
                            fnOnKeyDownFunction(tree, eventObject);
                            tree.gRecursive = false;
                            if (saveCurrent.parentNode == tree.currentTreeItem) 
                                tree.currentTreeItem = saveCurrent;
                        }
                    }
                }
                isEventHandled = true;
            }
            break;
        case 13: // Enter
            if (tree.inpField && tree.inpField.style.display != "none") {
                tree.inpField.blur();
                eventObject.isTreeRenamed = true;
            }
            else {
                if (selectedItem && !selectedItem.getTreeItem().isLeaf) 
                    if (selectedItem.getTreeItem().isOpen) 
                        selectedItem.getTreeItem().collapse();
                    else 
                        selectedItem.getTreeItem().expand();
            }
            isEventHandled = true;
            break;
        case 27: // Esc
            if (tree.inpField && tree.inpField.style.display != "none") {
                tree.inpField.cancelRename = true;
                tree.inpField.blur();
                isEventHandled = true;
            }
            break;
        case 67: // C
            if (tree.inpField && tree.inpField.style.display != "none") 
                return;
            else 
                if (eventObject.ctrlKey && selectedItem && (selectedItem.getTreeItem().itemType.updateAllowed == "true")) {
                    selectedItem.copy(true);
                    isEventHandled = true;
                }
            break;
        case 88: // X
            if (tree.inpField && tree.inpField.style.display != "none") 
                return;
            else 
                if (eventObject.ctrlKey && selectedItem && (selectedItem.getTreeItem().itemType.updateAllowed == "true")) {
                    selectedItem.cut(true);
                    isEventHandled = true;
                }
            break;
        case 86: // V 
            if (tree.inpField && tree.inpField.style.display != "none") 
                return;
            else 
                if (eventObject.ctrlKey && selectedItem && (selectedItem.getTreeItem().itemType.updateAllowed == "true")) {
                    selectedItem.paste();
                    isEventHandled = true;
                }
            break;
        case 113: // F2
            if (selectedItem && selectedItem.getTreeItem().itemType.updateAllowed == "true") {
                selectedItem.getTreeItem().rename();
                isEventHandled = true;
            }
            break;
        case 46: //DEL
            if (tree.inpField && tree.inpField.style.display != "none") 
                return;
            else 
                if (selectedItem && (selectedItem.getTreeItem().itemType.updateAllowed == "true")) {
                    selectedItem.remove();
                    isEventHandled = true;
                }
            break;
        default:
            break;
    }
    if (isEventHandled) {
        eventObject.stopPropagation();
        eventObject.preventDefault();
    }
}

//Function to more the focus to any element in the page once that element is clicked
function moveFocus(tree, focus){
    if (!tree.__moveFocus) {
        tree.__moveFocus = function(eventObject, focus, bActiveTree){
            //Get all selected items
            var selItems = tree.getSelectedItems();
            for (var i = 0; i < selItems.length; i++) {
                try {
                    var anchor = selItems[i].firstChild;
                    if (focus == false) {
                        if (anchor.outfocused) {
                            showHighlighted(anchor, true);
                            showOutfocused(anchor, false);
                        }
                    }
                    else {
                        showHighlighted(anchor, false);
                        showOutfocused(anchor, true);
                    }
                } 
                catch (e) {
                }
            }
            if (!bActiveTree) {
                activeTree = null;
                tree.active = false;
            }
        }
    }
    return tree.__moveFocus;
}

function delayedSelect(tree, elmt){
    if (!elmt) 
        return;
    if (!elmt.getTreeItem) 
        return;
    elmt.select(true);
    scrollIntoView(tree, elmt, false);
    tree.gKeyCurrItem = elmt;
    tree.ownerDocument.defaultView.setTimeout(function(){
        if (tree.gKeyCurrItem == elmt) {
            tree.gKeyCurrItem.select();
            tree.gKeyCurrItem = null;
        }
    }, 300);
}

function cancelEvent(tree){
    return function(eventObject){
        eventObject.preventDefault();
        eventObject.stopPropagation();
    }
}

function registerContext(tree, treeItem){
    if (activeTree != tree) {
        if (activeTree) 
            activeTree.active = false;
        tree.active = true;
        activeTree = tree;
    }
    //get handle to the ownerDocument of the page
    var gDocument = tree.ownerDocument.defaultView.document;
    // try to register treeItem to the contextmenu. If the contextmenu is not ready loading, wait for some time.
    var customEvent = new Object();
    customEvent.treeItem = treeItem;
    raiseEvent(tree, "oncontextregister", customEvent);
    
    if (customEvent.returnValue == false) 
        return;
    //get the context menu 
    var oContext = gDocument.getElementById(treeItem.itemType.contextMenu);
    //Check if the context menu is present in the ownerDocument. Then try to register
    try {
        oContext.registerHTML(treeItem);
    } 
    catch (e) {
        tree.ownerDocument.defaultView.setTimeout(function(){
            if (oContext) 
                oContext.registerHTML(treeItem);
        }, 2000)
    }
}

Tree.prototype.setRoot = function(oTreeRoot){
    if (oTreeRoot) {
        this.goTreeRoot = oTreeRoot;
    }
    else {
        this.goTreeRoot = null;
    }
    this.root = this.goTreeRoot;
}

Tree.prototype.getRoot = function(){
    return this.goTreeRoot;
}

Tree.prototype.setTreeData = function(oTreeData){
    this.root = null;
    if (typeof(oTreeData) == "string") {
        with (this.ownerDocument.defaultView) {
            var data = eval(oTreeData);
            this.TreeSchema.xmlData = data.XMLDocument ? data.XMLDocument : data;
        }
    }
    else {
        this.TreeSchema.xmlData = oTreeData;
    }
    
    // modification to set the property 'treeData' also for reading
    this.treeData = this.TreeSchema.xmlData;
    buildTree(this);
    this.selectedItems = new Array();
    this.selectedItem = null;
}

Tree.prototype.getTreeData = function(){
    return this.TreeSchema.xmlData;
}

//This function expands all the nodes and its sub nodes
function getExpandAllNodesFunction(tree){
    if (!tree.__getExpandAllNodesFunction) {
        tree.__getExpandAllNodesFunction = function(node){
            if (tree.leftFolding) {
                var scrollRight = tree.firstChild.offsetWidth - tree.clientWidth - tree.scrollLeft;
                scrollRight = scrollRight < 0 ? 0 : scrollRight;
            }
            
            //If we are in root, then set it as "node"
            if (!node) 
                node = this;
            expandAllNodes(node);
            
            if (tree.leftFolding) {
                tree.scrollLeft = tree.firstChild.offsetWidth - tree.clientWidth - scrollRight;
            }
        }
    }
    return tree.__getExpandAllNodesFunction;
}

function expandAllNodes(node, preferenceBased, tree, path){
    //first expand the node, then try to expand the children
    //this gives better performance
    if (!node.isLeaf) 
        node.expand();
    for (var child = node.lastChild.firstChild; child; child = child.nextSibling) {
        if (!child.isLeaf) {
            if (preferenceBased) {
                var level = parseInt(child.getAttribute("level")) - 1;
                var regEx = path.match(new RegExp(level + "--(.*?)##", "i"));
                var desc = _fngetTextContent(child.getLabel());
                if ((level == tree.lastExpandedItemLevel) && (desc == tree.lastExpandedItemDesc)) {
                    tree.lastExpandedItem = child;
                }
                if (RegExp.$1.indexOf(desc) >= 0) 
                    expandAllNodes(child, true, tree, path);
            }
            else {
                expandAllNodes(child);
            }
        }
    }
}

//This function collapses all the nodes and its sub nodes
function getCollapseAllNodesFunction(tree){
    if (!tree.__getCollapseAllNodesFunction) {
        tree.__getCollapseAllNodesFunction = function(node){
            //If we are in root, then set it as "node"
            if (!node) 
                node = this;
            collapseAllNodes(node);
        }
    }
    return tree.__getCollapseAllNodesFunction;
}

function collapseAllNodes(node){
    for (var child = node.lastChild.firstChild; child; child = child.nextSibling) {
        if (child.isLeaf == false) 
            collapseAllNodes(child);
    }
    //After collapsing all child items, collapse the node itself
    if (node) 
        node.collapse();
}

function setLabel(htmlAsString){
    var labelNode = this.firstChild.lastChild;
    labelNode.innerHTML = htmlAsString;
    var nodes = labelNode.childNodes;
    for (var ctr = 0; ctr < nodes.length; ctr++) {
        if (nodes[ctr].nodeType == 1) 
            nodes[ctr].getTreeItem = getTreeItem;
    }
    // To show the changed label in the input box if the label is in rename state
    var inputField = this.getTree().inpField;
    if (inputField && inputField.style.display != "none" && inputField.getTreeItem() == this) {
        inputField.value = _fngetTextContent(labelNode);
    }
}

/*
 Creates tree items from an array of dataNodes.
 the combination of concaternate and push in array is fastest.
 tree:			the active tree
 dataNodes:		array of dataNodes to be added to the parentItem
 parentItem:		the parent for the new items
 returnRequired:	true -> array is returned with the created tree items.
 false -> function returns nothing
 */
function createTreeLevelFromDataArray(tree, dataNodes, parentItem, returnRequired){
    var container = getNodeContainer(parentItem);
    var isRoot = parentItem.isTree ? true : false;
    setParentProperties(tree, parentItem);
    
    //create arrays for temp storeing node values, which can be attached after the innerHTML is written
    var newLevelArray = new Array();
    var isLeafArray = new Array();
    var isItemTypeArray = new Array();
    var boundDataNodes = new Array();
    var newLevel = parseInt(parentItem.getAttribute("level")) + 1;
    var setItemChecked;
    
    if (parentItem.itemType && parentItem.itemType.checkChildren == "true") {
        setItemChecked = parentItem.isChecked();
    }
    else {
        setItemChecked = false;
    }
    //create the string to update the innerHTML of the parent
    //a combination of string concatenate and string array push is used.
    //this is faster than just concatenate or just array push
    for (var i = 0; i < dataNodes.length; i++) {
        var node = dataNodes[i];
        var itemType = checkIfNode(tree, node);
        if (itemType) {
            boundDataNodes.push(node);
            isItemTypeArray.push(itemType);
            var isLeaf = isTreeItemALeaf(tree, node, itemType);
            isLeafArray.push(isLeaf);
            //create the html string for a single tree item		
            newLevelArray.push(createSingleNodeString(tree, node, itemType, isLeaf, newLevel, setItemChecked));
        }
    }
    
    if (newLevelArray.length > 0) {
        //write the innerHTML of the container of the parentItem
        if (container.firstChild) {
            insertHTML(container, "beforeEnd", newLevelArray.join(""));
        }
        else {
            container.innerHTML = newLevelArray.join("");
        }
    }
    var treeNode = container.lastChild;
    
    if (isRoot && !tree.root) 
        tree.setRoot(container.firstChild);
    
    //attach all the methods event properties to the created tree items
    i = boundDataNodes.length;
    while (i--) {
        attachEventsMethodsPropertiesSingleNode(tree, treeNode, boundDataNodes[i], isLeafArray[i], isItemTypeArray[i], isRoot);
        treeNode = treeNode.previousSibling;
    }
    newLevelArray = null;
    isLeafArray = null;
    isItemTypeArray = null;
    boundDataNodes = null;
    
    if (returnRequired) 
        return container.childNodes;
}

/* 
 Creates tree Items from a single dataNode with zero or more siblings
 the combination of concatenate and push in array is fastest
 tree:			the active tree
 dataNodes:		array of dataNodes to be added to the parentItem
 parentItem:		the parent for the new items
 */
function createTreeLevel(tree, dataNode, parentItem){
    var dataNodes = getXMLNodes(tree, dataNode, tree.TreeSchema.contSearch);
    createTreeLevelFromDataArray(tree, dataNodes, parentItem, false);
}

/* the following function to differntiate between reading treeData when the application is running in quirks mode with XSLpattern 
 (no local-name) or running in transitional mode
 */
function getXMLNodes(tree, node, path){
    if (tree.isQuirks && (!/local-name()/.test(path))) {
        return node.selectNodes(path); //NOMBV
    }
    else {
        return _fnselectXMLNodes(node, path);//._collection;
    }
}

/* the following function to differntiate between reading treeData when the application is running in quirks mode with XSLpattern 
 (no local-name) or running in transitional mode
 */
function getXMLNode(tree, node, path){
    if (tree.isQuirks && (!/local-name()/.test(path))) {
        return node.selectSingleNode(path); //NOMBV
    }
    else {
        return _fnselectXMLNode(node, path);
    }
}

/*
 Creates the HTML of the root node (the top tree item)
 tree:		the active tree
 parent:		the tree base (isTree=true) where the root is added to
 content:	description of the root node
 itemType:	object with the meta data of the root node
 */
function createRootNode(tree, parent, description, itemType){
    //description = Tree_HTMLUtil.getTranslatedLabel(description, tree);
    var iType = tree.TreeSchema.rootItem;
    if (itemType && typeof(itemType) != "undefined") 
        iType = itemType;
    
    var container = getNodeContainer(parent);
    var newNodeStr = "<li level='2' class='" + CL_ITEMSTANDALONECOLLAPSED + "'><a class='" + CL_ITEM_SMALL + "'>";
    
    if (iType.checkable == "true") {
        newNodeStr += "<input class='" + CL_ITEMCHECKBOX + "' type='checkbox' />";
    }
    
    newNodeStr += createEmptyImagesString(tree, iType) + "<span class='" + CL_ITEMDESCRIPTION + "'>" +
    description +
    "</span></a><ul class='" +
    CL_COLLAPSED +
    "'></ul></li>";
    container.innerHTML = newNodeStr;
    
    var root = container.firstChild;
    if (!tree.root) 
        tree.setRoot(root);
    attachEventsMethodsPropertiesSingleNode(tree, root, null, false, iType, true)
    
    return root;
}

/*
 Creates a string with the basic HTML tags of a single tree item
 tree:			the active tree
 dataNode:		dataNode of the new item
 itemType:		has general infrmation about the new node
 isLeaf:			is the item a leaf or a folder
 level:			level or depth of this item in the tree
 */
function createSingleNodeString(tree, dataNode, itemType, isLeaf, level, checked){
    var str = "<li level='" + level + "'><a class='" + CL_ITEM_SMALL + "'>";
    if (itemType.checkable == "true") {
        if (checked) {
            str += "<input class='" + CL_ITEMCHECKBOX + "' type='checkbox' checked />";
        }
        else {
            str += "<input class='" + CL_ITEMCHECKBOX + "' type='checkbox' />";
        }
    }
    str += createEmptyImagesString(tree, itemType) +
    "<span class='" +
    CL_ITEMDESCRIPTION +
    "'>" +
    getTreeNodeDescription(tree, itemType, dataNode) +
    "</span></a>" +
    getChildContainerString(tree, dataNode, isLeaf, level) +
    "</li>";
    
    return str;
}

/*Creates a string of "empty" image nodes for a tree item
 itemType:	object with the meta data of the treeItem
 */
function createEmptyImagesString(tree, itemType){
    var str = "";
    if (itemType) {
        var numIcons = itemType.icons.length;
        if (numIcons > 0) {
            for (var i = 0; i < numIcons; i++) {
                str += "<div class='" + CL_IMAGECONTAINER + "' draggable='false'></div>";
            }
        }
        else {
            if (itemType.icon && itemType.icon != "") {
                str += "<div class='" + CL_IMAGECONTAINER + "' draggable='false'></div>";
            }
        }
    }
    return str;
}

/*Sets the events, methods, properties, icons for a single tree Item
 tree:		the active tree
 treeNode:	tree item to be updated with events, ...
 isLeaf:		is tree item a leaf or a folder
 itemType	of the tree item
 isRoot:		is the tree item a root item
 */
function attachEventsMethodsPropertiesSingleNode(tree, treeNode, dataNode, isLeaf, itemType, isRoot){
    if (isRoot) {
        treeNode.isAtRootLevel = true;
        if (tree.getRoot() == treeNode) 
            treeNode.isRoot = true;
    }
    if (itemType != null) 
        treeNode.itemType = itemType;
    attachGetTreeItemToAllNodes(treeNode);
    treeNode.isLeaf = isLeaf;
    treeNode.isOpen = false;
    treeNode.getTree = getTreeFunction(tree);
    
    treeNode.getIcon = getIcon;
    treeNode.getLabel = getLabel;
    treeNode.setLabel = setLabel;
    treeNode.getChildContainer = getChildContainer;
    treeNode.getParentItem = getParent;
    treeNode.getItem = tree.getItem;
    treeNode.getItems = tree.getItems;
    treeNode.showItem = tree.showItem;
    treeNode.getCheckableItems = tree.getCheckableItems;
    
    setEvents(tree, treeNode);
    setMethods(tree, treeNode);
    treeNode.data = dataNode;
    if (treeNode.itemType && treeNode.itemType.contextMenu) {
        _fnaddListener(treeNode, "oncontextmenu", getSelectHandler(tree));
        registerContext(tree, treeNode);
    }
    else {
        _fnaddListener(treeNode, "oncontextmenu", cancelEvent(tree));
    }
    setPlusImage(tree, treeNode);
    setIcon(treeNode);
    
    var customEvent = new Object();
    customEvent.treeItem = treeNode;
    raiseEvent(tree, "onadditem", customEvent);
}

/* 
 attaches the getTreeItem function to all subnodes of treeNode
 treeItem:	item and its subnodes get function getTreeItem attached
 */
function attachGetTreeItemToAllNodes(treeItem){
    treeItem.getTreeItem = getTreeItem;
    var child = treeItem.firstChild;
    while (child) {
        child.getTreeItem = getTreeItem;
        child = child.nextSibling;
    }
    child = treeItem.firstChild.firstChild; //firstChild of <a>
    while (child) {
        child.getTreeItem = getTreeItem;
        if (child.tagName == "DIV" && child.firstChild) {
            child.firstChild.getTreeItem = getTreeItem;
        }
        child = child.nextSibling;
    }
}

// Returns the tree item name (which is shown to the user), the node having the name is in itemType.text //NOMBV
function getTreeNodeDescription(tree, itemType, dataNode){
    if (itemType && itemType.text) //NOMBV
    {
        var descNode;
        descNode = getXMLNode(tree, dataNode, itemType.text); //NOMBV
        if (descNode) {
            var description = _fngetTextContent(descNode);
            //description = Tree_HTMLUtil.getTranslatedLabel(description, tree);
            if (description && description != "") {
                return description.replace(/</g, "&lt;").replace(/>/g, "&gt;");
            }
        }
    }
    return dataNode.nodeName;
}

// Determines whether a tree item is a leaf or not
function isTreeItemALeaf(tree, dataNode, itemType){
    if (itemType && itemType.onexpand) 
        return false;
    if (dataNode && getXMLNode(tree, dataNode, tree.TreeSchema.contSearch)) {
        //has children -> not a leaf
        return false;
    }
    return true;
}

// Returns a string with the HTML definition of a container node
function getChildContainerString(tree, dataNode, isLeaf, level){
    if (isLeaf) 
        return "<ul class='" + CL_COLLAPSED + "'></ul>";
    if (tree.TreeSchema.useTreeLines && parseInt(level) > 2) {
        if (dataNode.nextSibling && checkIfNode(tree, dataNode.nextSibling) && !isLeaf) {
            return "<ul class='" + CL_COLLAPSED + " " + CL_CONNECTLINE + "'></ul>";
        }
    }
    return "<ul class='" + CL_COLLAPSED + "'></ul>";
}

// Update the properties of a parent, e.g. when a leaf gets a child, than it is no longer a leaf.
function setParentProperties(tree, parentItem){
    if (parentItem) {
        var container = getNodeContainer(parentItem);
        parentItem.childrenRendered = true;
        if (parentItem.isOpen && !isContainerExpanded(container)) {
            expandContainer(container);
        }
        if (parentItem.isLeaf) 
            leafToNode(tree, parentItem);
    }
}

//returns true when container is expanded (display="")
function isContainerExpanded(container){
    new RegExp(EXPANDED).test(container.className);
}

//shows container
function expandContainer(container){
    var exp = new RegExp(COLLAPSED);
    container.className = container.className.replace(exp, EXPANDED);
}

//hides container
function collapseContainer(container){
    var exp = new RegExp(EXPANDED);
    container.className = container.className.replace(exp, COLLAPSED);
}

// gets the tree from the passed node
function getTreeFromTreeNode(treeNode){
    //return (treeNode.nodeName=="tree" || treeNode.ownerDocument.defaultView.application.hasType(treeNode, TREE_LIBRARY)) ? treeNode : treeNode.getTree();
    if (treeNode.getAttribute("type") == "tree") 
        return treeNode;
    else {
        return treeNode.getTree();
    }
}

// Searches for a particular treeItem with the description itemText. The search is performed in
// the available HTML.
function searchForItemInHTML(treeItem, itemText, inSensitive){
    if (inSensitive) {
        var labelNodes = treeItem.getElementsByTagName("SPAN");
        var length = labelNodes.length;
        var iText = itemText.toLowerCase();
        for (var i = 0; i < length; i++) {
            var textNode = labelNodes[i];
            if (textNode && _fngetTextContent(textNode).toLowerCase() == iText) {
                return textNode.getTreeItem();
            }
        }
    }
    else {
        var labelNodes = treeItem.getElementsByTagName("SPAN");
        var length = labelNodes.length;
        for (var i = 0; i < length; i++) {
            var textNode = labelNodes[i];
            if (textNode && _fngetTextContent(textNode) == itemText) {
                return textNode.getTreeItem();
            }
        }
    }
    return null;
}

//Recursive function searches for treeItems which have no rendered children. Adds these to array
function getTreeBottomItems(tree, treeItem){
    do {
        var container = getNodeContainer(treeItem);
        if (container.firstChild) {
            getTreeBottomItems(tree, container.firstChild);
        }
        else {
            tree.treeItemBottomItemInHTMLArray.push(treeItem);
        }
        treeItem = treeItem.nextSibling;
    }
    while (treeItem);
}

// Fills the array: tree.treeItemBottomItemInHTMLArray
// with the treeItems which are at the lowest rendered level in the present HTML
function doGetTreeBottomItems(tree, treeItem){
    var container = getNodeContainer(treeItem);
    if (container.firstChild) {
        getTreeBottomItems(tree, container.firstChild);
    }
    else {
        tree.treeItemBottomItemInHTMLArray.push(treeItem);
    }
}

// checks whether the passNode has any treeItems with data that contains the itemText
function checkBottomTreeItemsDataNodes(tree, passNode, itemText, inSensitive){
    tree.treeItemBottomItemInHTMLArray = new Array();
    
    //The next function collects all the treeItems at the lowest rendered level in the HTML
    doGetTreeBottomItems(tree, passNode);
    //The data nodes of lowest level treeItems will be checked for presents of itemText
    var i = 0;
    while (!tree.foundSearchItem && i < tree.treeItemBottomItemInHTMLArray.length) {
        var dataNode;
        if (tree.treeItemBottomItemInHTMLArray[i].isRoot && tree.treeItemBottomItemInHTMLArray[i].data == null) 
            dataNode = tree.TreeSchema.xmlData;
        else 
            dataNode = tree.treeItemBottomItemInHTMLArray[i].data;
        
        doDataNodeSearch(tree, dataNode, itemText, inSensitive);
        if (tree.foundSearchItem) 
            tree.itemHavingDataWithTreeItem = tree.treeItemBottomItemInHTMLArray[i];
        i++;
    }
    //tree.foundSearchItem =true means the itemText exists in the dataNode -> can start rendering the HTML
    
    tree.treeItemBottomItemInHTMLArray = null;
}

// Searches for a dataNode which has the description of itemText. 
// tree.foundSearchItem will be set true, if the dataNode is found
function doDataNodeSearch(tree, dataNode, itemText, inSensitive){
    tree.foundSearchItem = false;
    if (dataNode != null) {
        setFoundSearchItem(tree, dataNode, itemText, inSensitive);
        if (!tree.foundSearchItem) {
            var fChild = getXMLNode(tree, dataNode, ".//" + tree.TreeSchema.contSearch);
            searchForItemInDataNode(tree, fChild, itemText, inSensitive);
        }
    }
}

// Traverses the dataNode im search for itemText.Stops the moment the dataNode is found.
function searchForItemInDataNode(tree, dataNode, itemText, inSensitive){
    if (dataNode) 
        do {
            setFoundSearchItem(tree, dataNode, itemText, inSensitive);
            if (!tree.foundSearchItem) {
                var fChild = getXMLNode(tree, dataNode, ".//" + tree.TreeSchema.contSearch);
                searchForItemInDataNode(tree, fChild, itemText, inSensitive);
            }
            if (dataNode && !tree.foundSearchItem) 
                dataNode = dataNode.nextSibling;
        }
        while (dataNode && !tree.foundSearchItem);
}

// Checks for a single dataNode whether it has the itemText or not.
function setFoundSearchItem(tree, dataNode, itemText, inSensitive){
    var itemType = checkIfNode(tree, dataNode);
    if (itemType && itemType != null) {
        var descNode = getXMLNode(tree, dataNode, itemType.text); //NOMBV
        if (descNode && _fngetTextContent(descNode)) {
            if (inSensitive) {
                if (_fngetTextContent(descNode).toLowerCase() == itemText.toLowerCase()) 
                    tree.foundSearchItem = true;
            }
            else {
                if (_fngetTextContent(descNode) == itemText) 
                    tree.foundSearchItem = true;
            }
        }
    }
}

// Searched for a treeNode with the description itemText. The search is done under treeItem.
// This function renders the HTML and stops rendering when node is found.
function searchItemInHTMLTree(tree, treeItem, itemText, inSensitive){
    //render children
    if (!treeItem.isLeaf) {
        var container = getNodeContainer(treeItem);
        
        //children not available render them
        if (!container.firstChild) 
            renderChildren(treeItem);
        
        //see if one of the children is the item we look for
        var child = container.firstChild;
        while (child && tree.resultGetItem == null) {
            var nodeText = _fngetTextContent(child.getLabel());
            if (inSensitive) {
                if (nodeText.toLowerCase() == itemText.toLowerCase()) 
                    tree.resultGetItem = child;
            }
            else {
                if (nodeText == itemText) 
                    tree.resultGetItem = child;
            }
            child = child.nextSibling;
        }
        //not found in child level of treeItem, check the children of the children
        child = container.firstChild;
        while (child && tree.resultGetItem == null) {
            searchItemInHTMLTree(tree, child, itemText, inSensitive);
            child = child.nextSibling;
        }
    }
}

// New function which renders the HTML of children of the treeNode
function renderChildren(treeNode){
    createTreeLevel(getTreeFromTreeNode(treeNode), treeNode.data, treeNode);
    
    treeNode.childrenRendered = true;
}

/*
 Returns the first treeItem that has the description of itemText (casesensitive or not).
 The order of searching is:
 1: Search the available html under treeItem. If found return result.
 2: searchFullTree=true, check if itemText is present in the treeItem.data. If NOT return null, else continue
 3: itemText is present in treeItem.data. Render HTML until the searched treeItem is rendered.
 */
function searchItemInTree(treeItem, itemText, inSensitive, searchFullTree){
    //search in available HTML (under treeItem)
    var htmlSearch = searchForItemInHTML(treeItem, itemText, inSensitive);
    if (htmlSearch != null) {
        return htmlSearch;
    }
    if (!searchFullTree) {
        //no item found in the html, and no further searching required
        return null;
    }
    //not found in existing HTML, further searching required.
    //Check whether the itemText is present in the treeItem.data, the dataNode of the treeItem
    var tree = getTreeFromTreeNode(treeItem);
    var passNode = null;
    
    if (treeItem == tree) {
        // fix for the bug in tree with no root element and multiple elements
        // parallel to each other at the root level
        passNode = tree.root.nextSibling != null ? treeItem : tree.root;
    }
    else 
        passNode = treeItem;
    
    tree.foundSearchItem = false;
    //The treeItem.data, of the treeItems available, at the lowest rendered levels
    //need to be checked for presents of itemText
    checkBottomTreeItemsDataNodes(tree, passNode, itemText, inSensitive);
    
    tree.resultGetItem = null;
    if (tree.foundSearchItem) {
        //itemText is available in treeItem.data -> render HTML to return the treeItem with itemText
        searchItemInHTMLTree(tree, tree.itemHavingDataWithTreeItem, itemText, inSensitive);
        tree.foundSearchItem = false; //reset for next search
        tree.itemHavingDataWithTreeItem = null;
    }
    return tree.resultGetItem;
}

/*
 Searches for all the treeItems under treeNode with description itemText.
 First a check is done whether the itemText is available at all in the treeNode.data.
 If this is not the case than further searching has no point.
 However, when a node is found, then all the underlying html needs to be rendered.
 */
function searchItemsInTree(treeItem, itemText, inSensitive, searchFullTree){
    // This function searches for a treeItem given the text of that item.
    // It returns all items found (optional case-insensitive), within an array as objects of type TreeItem.
    
    var foundArray = new Array();
    if (typeof(searchFullTree) == "undefined") 
        searchFullTree = false;
    
    //for this function to work, the entier html tree must have been rendered
    if (searchFullTree == true) {
        //check if itemText exists at all in:
        //  data of the (treeItems at the lowest level )
        
        var tree = getTreeFromTreeNode(treeItem);
        var passNode = null;
        
        if (treeItem == tree && tree.root) {
            passNode = tree.root;
        }
        else {
            passNode = treeItem;
        }
        
        tree.foundSearchItem = false;
        //The treeItem.data, of the treeItems available, at the lowest rendered levels
        //need to be checked for presents of itemText
        checkBottomTreeItemsDataNodes(tree, passNode, itemText, inSensitive);
        
        
        if (tree.foundSearchItem) {
            //itemText is, at least once, available. Start rendering all the underlying HTML
            //TODO render only those having the data -> all dataNodes have to be searched
            
            if (treeItem == tree && tree.root) 
                renderSubTree(treeItem.root);
            else 
                renderSubTree(treeItem);
            tree.itemHavingDataWithTreeItem = null;
            tree.foundSearchItem = false;
        }
    }
    //HTML rendered, now check the html for itemText nodes
    if (typeof(inSensitive) == "undefined") 
        inSensitive = false;
    
    if (inSensitive) {
        var labelNodes = treeItem.getElementsByTagName("SPAN");
        var length = labelNodes.length;
        var iText = itemText.toLowerCase();
        for (var i = 0; i < length; i++) {
            var textNode = labelNodes[i];
            if (textNode && _fngetTextContent(textNode).toLowerCase() == iText) {
                foundArray[foundArray.length] = textNode.getTreeItem();
            }
        }
    }
    else {
        var labelNodes = treeItem.getElementsByTagName("SPAN");
        var length = labelNodes.length;
        for (var i = 0; i < length; i++) {
            var textNode = labelNodes[i];
            if (textNode && _fngetTextContent(textNode) == itemText) {
                foundArray[foundArray.length] = textNode.getTreeItem();
            }
        }
    }
    if (foundArray.length > 0) 
        return foundArray;
    return null;
}

// renders all the html until the treeitem.
// Recursive function
function renderSubTree(treeItem){
    if (!treeItem.isLeaf) {
        var container = getNodeContainer(treeItem);
        if (!container.firstChild) 
            renderChildren(treeItem);
        
        var child = container.firstChild;
        while (child) {
            renderSubTree(child);
            child = child.nextSibling;
        }
    }
}

function showHighlighted(anchor, bSelect){
    if (!anchor) 
        return;
    var spn = anchor.lastChild;
    if (bSelect) {
        HTMLUtil.addClassName(CL_HIGHLIGHT, spn);
    }
    else {
        HTMLUtil.removeClassName(CL_HIGHLIGHT, spn);
    }
}

function showOutfocused(anchor, bSelect){
    if (!anchor) 
        return;
    var spn = anchor.lastChild;
    if (bSelect) {
        HTMLUtil.addClassName(CL_OUTFOCUS, spn);
        anchor.outfocused = true;
    }
    else {
        HTMLUtil.removeClassName(CL_OUTFOCUS, spn);
        anchor.outfocused = false;
    }
}

/*prop=true and prop="true" returns true
 prop=false and prop="false" returns false, else null
 */
function readProperty(prop){
    if (typeof(prop) == "undefined") 
        return null;
    if (typeof(prop) == "string") {
        if (prop.toLowerCase() == "true") 
            return true;
        if (prop.toLowerCase() == "false") 
            return false;
    }
    else {
        if (prop == true) 
            return true;
        if (prop == false) 
            return false;
    }
    return null;
}

function bindTreeData(tree, dataModel){
    return function(eventObject){
        tree.setTreeData(dataModel.getData());
    }
}

function insertHTML(element, where, htmlStr) {
	if ( element && typeof(element) != "undefined" ) {
		if ( window.ActiveXObject )	{
			element.insertAdjacentHTML(where, htmlStr); //NOMBV
		} else {
			var fragment = element.ownerDocument.createDocumentFragment();
			var dummyDiv = element.ownerDocument.createElement("div");
			dummyDiv.innerHTML = htmlStr;
			while ( dummyDiv.firstChild ) {
				fragment.appendChild(dummyDiv.firstChild);
			}
			switch (where) {
				case 'beforeBegin':
					element.parentNode.insertBefore(fragment,element);
					break;
				case 'afterBegin':
					element.insertBefore(fragment,element.firstChild);
					break;
				case 'beforeEnd':
					element.appendChild(fragment);
					break;
				case 'afterEnd':
					if (element.nextSibling) 
						element.parentNode.insertBefore(fragment,element.nextSibling);
					else 
						element.parentNode.appendChild(fragment);
					break;
			}
		}
	}
}
	
// Helper function for Event library.
function fireListener(sourceObject, eventObject, eventListener, eventName, currentWindow){
    var listener = eventListener.listener;
    if (!listener) 
        return;
    
    //Sometimes a 'function' will give "object" as type. Additional check added for functionpointer having
    //the prototype attribute which also mean it is a function.
    var context = eventListener.context;
    if (typeof(listener) == "function" || typeof(listener) == "object") {
        try {
            if (typeof(listener) == "function" || (typeof(listener) == "object" && listener.prototype)) {
                listener.call(context || currentWindow, eventObject, context);
            }
        } 
        catch (error) {
            // "Can't execute code from a freed script" error.
            // Also "callee server not server application disappeared or invalid" error.
            if (error.number == -2146823277 || error.number == -2147418094 || error.number == -2147417848) {
                sourceObject.removeListener(eventName, listener, context);
            }
            else {
            }
        }
    }
    else 
        if (typeof(listener) == "string") {
            // Only execute the listener if the source object is an HTML element inside a document.
            if (currentWindow) {
                with (currentWindow) {
                    if (window.execScript) 
                        execScript("_TMP_FOR_EVAL_ = function (event, context){" + listener + "}");
                    else 
                        eval("_TMP_FOR_EVAL_ = function (event, context){" + listener + "}");
                    _TMP_FOR_EVAL_.call(currentWindow, eventObject, context);
                    _TMP_FOR_EVAL_ = null; //To Avoid leaks in other window;
                }
            }
        }
        else {
            alert("Event listener " + eventName + " ignored " + eventListener.toString());
        }
}

function raiseEvent(treeElm, eventName, eventObject){
    // only events possible when the _eventListeners array is available (this can be removed when the object is
    //  cleaned up).
    if (!treeElm._eventListeners) 
        return;
    
    if (!eventObject) 
        eventObject = new Object();
    if (!eventObject.srcElement) 
        eventObject.srcElement = treeElm;
    if (!eventObject.type) 
        eventObject.type = eventName.substring(2);
    eventObject.__cordysCustomEvent = true;
    
    var currentApplicationEvent;
    var currentDocument = treeElm.ownerDocument;
    var currentWindow = currentDocument ? currentDocument.defaultView : null;
    var currentApplication = currentWindow ? currentWindow.application : null;
    // Only set the application event when we actually can get hold of an application.
    if (currentApplication) {
        // Stack the current application event and restore it later on.
        currentApplicationEvent = currentApplication.event;
        currentApplication.event = eventObject;
    }
    
    var mainListener = {
        listener: typeof(treeElm.getAttribute) != "undefined" && !treeElm[eventName] ? treeElm.getAttribute(eventName) : treeElm[eventName],
        context: treeElm
    };
    fireListener(treeElm, eventObject, mainListener, eventName, currentWindow);
    //In the above event handler, the application holding the object may be closed. Also in slower machines
    // the object is already set to null
    if (typeof(treeElm._eventListeners) == "unknown" || !treeElm._eventListeners) 
        return;
    // Now fire on the registered listeners
    var eventListeners = treeElm._eventListeners[eventName];
    if (eventListeners) {
        for (var i = 0; i < eventListeners.length; i++) {
            var additionalListener = eventListeners[i];
            while (!additionalListener && i < eventListeners.length) {
                var newLength = eventListeners.length - 1;
                additionalListener = eventListeners[i] = eventListeners[newLength];
                eventListeners[newLength] = null;
                eventListeners.length--;
            }
            // Check for the additionalListener if the intermediate event handlers closes the application causing script errors.
            if (additionalListener && (additionalListener.listener != mainListener.listener || additionalListener.context != mainListener.context)) {
                fireListener(treeElm, eventObject, additionalListener, eventName, currentWindow);
            }
        }
    }
    
    // If necessary and available, restore the previous application event.
    if (typeof(treeElm.ownerDocument) != "unknown" && currentApplication) {
        currentApplication.event = currentApplicationEvent;
    }
}

function dataTransferSetData(eventObject, type, data){
    var nativeData = null;
    if (eventObject) {
        eventObject.dataTransfer.setData(type, data); // Necessary for IE and to allow external droppable data //NOMBV
    }
    if (!dataTransfer.__internalDataTransfer) {
        // Make internal dataTransfer buffer (This will not be visible from external browser instances)
        dataTransfer.__internalDataTransfer = new Object();
    }
    dataTransfer.__internalDataTransfer[type] = data;
}

function dataTransferGetData(eventObject, type){
    var data = null;
    if (eventObject) {
        data = eventObject.dataTransfer.getData(type); //NOMBV
    }
    if (!data && dataTransfer.__internalDataTransfer) {
        data = dataTransfer.__internalDataTransfer[type];
    }
    return data;
}

/******************************** HTML Utilities *******************************************/
function HTMLUtil(){
}

HTMLUtil.removeClassName = function(className, element){
    if (element) {
        var newClassName = HTMLUtil.getClassName(element).replace(new RegExp("\\b" + className + "\\b"), "");
        HTMLUtil.setClassName(newClassName, element);
    }
}

HTMLUtil.addClassName = function(className, element){
    if (element && !HTMLUtil.hasClassName(className, element)) {
        className = (HTMLUtil.getClassName(element) + " " + className).replace(/^\s+|\s+$/g, "");
        HTMLUtil.setClassName(className, element);
    }
}

HTMLUtil.hasClassName = function(className, element){
    if (element) {
        return new RegExp("\\b" + className + "\\b").test(element.className);
    }
    return false;
}

//	Returns the string value of the list of CSS classes currently assigned directly 
//	to the node in question. Returns an empty string if no class attribute is found;
HTMLUtil.getClassName = function(element){
    return (element) ? element.className.replace(/^\s+|\s+$/g, "") : "";
}

//	Clobbers the existing list of classes for the node, replacing it with
//	the list given in the 2nd argument.
HTMLUtil.setClassName = function(className, element){
    if (element) {
        element.className = className;
    }
}